[PATCH v1 09/24] s390/vfio-ap: Return value of sysfs migratable attribute from VFIO_DEVICE_GET_INFO ioctl

From: Anthony Krowiak

Date: Wed Mar 25 2026 - 17:04:32 EST


Extends the 'vfio_device_info' object passed to the VFIO_DEVICE_GET_INFO
ioctl to include a vfio device capability specifying the value of the
mdev's sysfs 'migratable' attribute. This value may be used to enable or
block live guest migration when the vfio device is realized in userspace.

Signed-off-by: Anthony Krowiak <akrowiak@xxxxxxxxxxxxx>
---
drivers/s390/crypto/vfio_ap_ops.c | 75 ++++++++++++++++++++++++++++++-
1 file changed, 73 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 213832263dc9..d487628027c7 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -19,6 +19,7 @@
#include <linux/uuid.h>
#include <asm/kvm.h>
#include <asm/zcrypt.h>
+#include <uapi/linux/vfio_ap.h>

#include "vfio_ap_private.h"
#include "vfio_ap_debug.h"
@@ -2183,11 +2184,75 @@ int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix)
return num_queues;
}

-static int vfio_ap_mdev_get_device_info(unsigned long arg)
+static int vfio_ap_add_caps_to_dev_info(struct vfio_device_info *dev_info,
+ unsigned long arg,
+ struct vfio_info_cap *caps)
{
+ lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+ if (caps->size) {
+ dev_info->flags |= VFIO_DEVICE_FLAGS_CAPS;
+ if (dev_info->argsz < sizeof(*dev_info) + caps->size) {
+ dev_info->argsz = sizeof(*dev_info) + caps->size;
+ } else {
+ vfio_info_cap_shift(caps, sizeof(*dev_info));
+
+ if (copy_to_user((void __user *)arg + sizeof(*dev_info),
+ caps->buf, caps->size)) {
+ kfree(caps->buf);
+ return -EFAULT;
+ }
+
+ dev_info->cap_offset = sizeof(*dev_info);
+ }
+
+ kfree(caps->buf);
+ }
+
+ return 0;
+}
+
+static int vfio_ap_add_attrs_cap_to_caps(struct ap_matrix_mdev *matrix_mdev,
+ struct vfio_info_cap *caps)
+{
+ struct vfio_device_info_cap_ap_attrs cap;
+
+ lockdep_assert_held(matrix_dev->mdevs_lock);
+ cap.migratable = matrix_mdev->migratable;
+ cap.header.id = VFIO_DEVINFO_CAP_AP_ATTRS_ID;
+ cap.header.version = VFIO_DEVINFO_CAP_AP_ATTRS_VERSION;
+
+ return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
+}
+
+static int vfio_ap_add_caps(struct ap_matrix_mdev *matrix_mdev,
+ unsigned long arg,
+ struct vfio_device_info *dev_info)
+{
+ struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
+ int ret = 0;
+
+ lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+ ret = vfio_ap_add_attrs_cap_to_caps(matrix_mdev, &caps);
+ if (ret)
+ return ret;
+
+ ret = vfio_ap_add_caps_to_dev_info(dev_info, arg, &caps);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int vfio_ap_mdev_get_device_info(struct ap_matrix_mdev *matrix_mdev,
+ unsigned long arg)
+{
+ int ret;
unsigned long minsz;
struct vfio_device_info info;

+ lockdep_assert_held(&matrix_dev->mdevs_lock);
minsz = offsetofend(struct vfio_device_info, num_irqs);

if (copy_from_user(&info, (void __user *)arg, minsz))
@@ -2200,6 +2265,12 @@ static int vfio_ap_mdev_get_device_info(unsigned long arg)
info.num_regions = 0;
info.num_irqs = VFIO_AP_NUM_IRQS;

+ ret = vfio_ap_add_caps(matrix_mdev, arg, &info);
+ if (ret)
+ return ret;
+
+ minsz = min_t(size_t, info.argsz, sizeof(info));
+
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
}

@@ -2395,7 +2466,7 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,
mutex_lock(&matrix_dev->mdevs_lock);
switch (cmd) {
case VFIO_DEVICE_GET_INFO:
- ret = vfio_ap_mdev_get_device_info(arg);
+ ret = vfio_ap_mdev_get_device_info(matrix_mdev, arg);
break;
case VFIO_DEVICE_RESET:
ret = vfio_ap_mdev_reset_queues(matrix_mdev);
--
2.52.0