From: Anthony Krowiak <akrowiak@linux.ibm.com>
To: linux-s390@vger.kernel.org, linux-kernel@vger.kernel.org,
kvm@vger.kernel.org
Cc: jjherne@linux.ibm.com, borntraeger@de.ibm.com,
mjrosato@linux.ibm.com, pasic@linux.ibm.com, alex@shazbot.org,
kwankhede@nvidia.com, fiuczy@linux.ibm.com, pbonzini@redhat.com,
frankja@linux.ibm.com, imbrenda@linux.ibm.com,
agordeev@linux.ibm.com, hca@linux.ibm.com, gor@linux.ibm.com
Subject: [PATCH v10 07/15] s390/vfio-ap: File ops called to save the vfio device migration state
Date: Fri, 23 Oct 2026 13:43:41 -0400 [thread overview]
Message-ID: <20261023174349.3545941-8-akrowiak@linux.ibm.com> (raw)
In-Reply-To: <20261023174349.3545941-1-akrowiak@linux.ibm.com>
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 bc216c51a1a7..f9b00fb89410 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 */
@@ -140,14 +141,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 ap_matrix_mdev *matrix_mdev = filp->private_data;
@@ -162,6 +155,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
+ *
+ * @filp: Pointer to the file stream used to read the vfio-ap device state
+ * @len: The length of the data to be read
+ *
+ * Verify the following:
+ * - @filp private data is an ap_matrix_mdev instance
+ * - @filp is the instance opened when state transitioned from STOP to STOP_COPY
+ * - @filp->f_pos + @len does not cause integer overflow
+ *
+ * Returns: 0 if the parameters pass validation; otherwise returns an error
+ */
+static int validate_stop_copy_read_parms(struct file *filp, size_t len)
+{
+ struct vfio_ap_migration_data *mig_data;
+ struct ap_matrix_mdev *matrix_mdev;
+ loff_t total_len;
+
+ lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+ if (check_add_overflow((loff_t)len, filp->f_pos, &total_len))
+ return -EIO;
+
+ /*
+ * matrix_mdev 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 matrix_mdev cannot
+ * be freed while this file descriptor is open.
+ */
+ matrix_mdev = filp->private_data;
+
+ if (!matrix_mdev->mig_data)
+ return -ENODEV;
+
+ mig_data = matrix_mdev->mig_data;
+
+ if (mig_data->stop_copy_mig_file.filp != filp)
+ return -EINVAL;
+
+ return 0;
+}
+
+static size_t vfio_ap_config_size(struct ap_matrix_mdev *matrix_mdev,
+ 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;
+ struct ap_matrix_mdev *matrix_mdev;
+ 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;
+
+ mutex_lock(&matrix_dev->mdevs_lock);
+ pos = &filp->f_pos;
+
+ ret = validate_stop_copy_read_parms(filp, len);
+ if (ret) {
+ mutex_unlock(&matrix_dev->mdevs_lock);
+ return ret;
+ }
+
+ matrix_mdev = filp->private_data;
+ mig_file = &matrix_mdev->mig_data->stop_copy_mig_file;
+
+ /*
+ * 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(matrix_mdev);
+ if (ret) {
+ mutex_unlock(&matrix_dev->mdevs_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);
+ 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);
+ if (len == 0) {
+ mutex_unlock(&matrix_dev->mdevs_lock);
+ return 0;
+ }
+
+ /*
+ * 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);
+ return -ENOMEM;
+ }
+
+ memcpy(bounce, (char *)mig_file->ap_config + *pos, len);
+ *pos += len;
+ mutex_unlock(&matrix_dev->mdevs_lock);
+
+ if (copy_to_user(buf, bounce, len)) {
+ kfree(bounce);
+ return -EFAULT;
+ }
+
+ 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
next prev parent reply other threads:[~2026-10-23 17:44 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 17:43 [PATCH v10 00/15] s390/vfio-ap: Add live guest migration support Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 01/15] s390/vfio-ap: Provide function to get the number of queues assigned to mdev Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 02/15] s390/vfio-ap: Data structures for facilitating vfio device migration Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 03/15] s390/vfio-ap: Functions to initialize/release vfio device migration data Anthony Krowiak
2026-10-24 7:11 ` kernel test robot
2026-10-23 17:43 ` [PATCH v10 04/15] s390/vfio-ap: Reset migration state in VFIO_DEVICE_RESET ioctl handler Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 05/15] s390/vfio-ap: Callback to get/set vfio device mig state during guest migration Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 06/15] s390/vfio-ap: Transition guest migration state from STOP to STOP_COPY Anthony Krowiak
2026-10-23 17:43 ` Anthony Krowiak [this message]
2026-10-23 17:43 ` [PATCH v10 08/15] s390/vfio-ap: Transition device migration state from STOP to RESUMING Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 09/15] s390/vfio-ap: Prepare lock helpers and matrix init for cross-file use Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 10/15] s390/vfio-ap: File ops called to resume the vfio device migration Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 11/15] s390/vfio-ap: Transition device migration state to from/to STOP Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 12/15] s390/vfio-ap: Callback to get the size of data to be migrated during guest migration Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 13/15] s390/vfio-ap: Set up for vfio-ap device migration Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 14/15] s390/vfio-ap: Add 'migration' feature to sysfs 'features' attribute Anthony Krowiak
2026-10-23 17:43 ` [PATCH v10 15/15] s390/vfio-ap: Add live guest migration chapter to vfio-ap.rst Anthony Krowiak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261023174349.3545941-8-akrowiak@linux.ibm.com \
--to=akrowiak@linux.ibm.com \
--cc=agordeev@linux.ibm.com \
--cc=alex@shazbot.org \
--cc=borntraeger@de.ibm.com \
--cc=fiuczy@linux.ibm.com \
--cc=frankja@linux.ibm.com \
--cc=gor@linux.ibm.com \
--cc=hca@linux.ibm.com \
--cc=imbrenda@linux.ibm.com \
--cc=jjherne@linux.ibm.com \
--cc=kvm@vger.kernel.org \
--cc=kwankhede@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=mjrosato@linux.ibm.com \
--cc=pasic@linux.ibm.com \
--cc=pbonzini@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®