AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/26 08:49 flow patch-triage
2m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch implements live guest migration for the s390 vfio_ap (Adjunct Processor) mediated device driver. The vfio_ap driver requires physical IBM Z AP cryptographic hardware to be present on the host system. During module initialization, `vfio_ap_init()` checks `ap_instructions_available()`, which executes a hardware `PQAP` instruction. If the hardware does not support it, the driver returns `-ENODEV` and fails to load. Standard QEMU/KVM environments do not emulate AP cryptographic hardware in software. Therefore, the code paths introduced in this patch are impossible to reach in standard virtualized environments without specific physical hardware, making it unsuitable for fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/07/26 08:49 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit c925b396b5604e9b7562291129db04b592192ec8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sun Jul 26 08:49:10 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/arch/s390/vfio-ap.rst b/Documentation/arch/s390/vfio-ap.rst\nindex ac0c07f76ddd7b..02d7cbe5e47918 100644\n--- a/Documentation/arch/s390/vfio-ap.rst\n+++ b/Documentation/arch/s390/vfio-ap.rst\n@@ -1012,7 +1012,7 @@ guest_matrix dyn ap_config\n \n the following features are advertised:\n \n----------------+---------------------------------------------------------------+\n++--------------+---------------------------------------------------------------+\n | Flag         | Description                                                   |\n +==============+===============================================================+\n | guest_matrix | guest_matrix attribute exists. It reports the matrix of       |\n@@ -1021,105 +1021,523 @@ the following features are advertised:\n +--------------+---------------------------------------------------------------+\n | dyn          | Indicates hot plug/unplug of AP adapters, domains and control |\n |              | domains for a guest to which the mdev is attached.            |\n-+------------+-----------------------------------------------------------------+\n++--------------+---------------------------------------------------------------+\n | ap_config    | ap_config interface for one-shot modifications to mdev config |\n +--------------+---------------------------------------------------------------+\n+| migratable   | Indicates that live guest migration is supported for guests   |\n+|              | to which crypto devices are passed through                    |\n++--------------+---------------------------------------------------------------+\n \n-Limitations\n-===========\n-Live guest migration is not supported for guests using AP devices without\n-intervention by a system administrator. Before a KVM guest can be migrated,\n-the vfio_ap mediated device must be removed. Unfortunately, it can not be\n-removed manually (i.e., echo 1 \u003e /sys/devices/vfio_ap/matrix/$UUID/remove) while\n-the mdev is in use by a KVM guest. If the guest is being emulated by QEMU,\n-its mdev can be hot unplugged from the guest in one of two ways:\n-\n-1. If the KVM guest was started with libvirt, you can hot unplug the mdev via\n-   the following commands:\n-\n-      virsh detach-device \u003cguestname\u003e \u003cpath-to-device-xml\u003e\n-\n-      For example, to hot unplug mdev 62177883-f1bb-47f0-914d-32a22e3a8804 from\n-      the guest named 'my-guest':\n-\n-         virsh detach-device my-guest ~/config/my-guest-hostdev.xml\n-\n-            The contents of my-guest-hostdev.xml:\n-\n-.. code-block:: xml\n-\n-            \u003chostdev mode='subsystem' type='mdev' managed='no' model='vfio-ap'\u003e\n-              \u003csource\u003e\n-                \u003caddress uuid='62177883-f1bb-47f0-914d-32a22e3a8804'/\u003e\n-              \u003c/source\u003e\n-            \u003c/hostdev\u003e\n-\n-\n-      virsh qemu-monitor-command \u003cguest-name\u003e --hmp \"device-del \u003cdevice-id\u003e\"\n-\n-      For example, to hot unplug the vfio_ap mediated device identified on the\n-      qemu command line with 'id=hostdev0' from the guest named 'my-guest':\n-\n-.. code-block:: sh\n-\n-         virsh qemu-monitor-command my-guest --hmp \"device_del hostdev0\"\n-\n-2. A vfio_ap mediated device can be hot unplugged by attaching the qemu monitor\n-   to the guest and using the following qemu monitor command:\n-\n-      (QEMU) device-del id=\u003cdevice-id\u003e\n-\n-      For example, to hot unplug the vfio_ap mediated device that was specified\n-      on the qemu command line with 'id=hostdev0' when the guest was started:\n-\n-         (QEMU) device-del id=hostdev0\n-\n-After live migration of the KVM guest completes, an AP configuration can be\n-restored to the KVM guest by hot plugging a vfio_ap mediated device on the target\n-system into the guest in one of two ways:\n-\n-1. If the KVM guest was started with libvirt, you can hot plug a matrix mediated\n-   device into the guest via the following virsh commands:\n-\n-   virsh attach-device \u003cguestname\u003e \u003cpath-to-device-xml\u003e\n-\n-      For example, to hot plug mdev 62177883-f1bb-47f0-914d-32a22e3a8804 into\n-      the guest named 'my-guest':\n-\n-         virsh attach-device my-guest ~/config/my-guest-hostdev.xml\n-\n-            The contents of my-guest-hostdev.xml:\n-\n-.. code-block:: xml\n-\n-            \u003chostdev mode='subsystem' type='mdev' managed='no' model='vfio-ap'\u003e\n-              \u003csource\u003e\n-                \u003caddress uuid='62177883-f1bb-47f0-914d-32a22e3a8804'/\u003e\n-              \u003c/source\u003e\n-            \u003c/hostdev\u003e\n-\n-\n-   virsh qemu-monitor-command \u003cguest-name\u003e --hmp \\\n-   \"device_add vfio-ap,sysfsdev=\u003cpath-to-mdev\u003e,id=\u003cdevice-id\u003e\"\n-\n-      For example, to hot plug the vfio_ap mediated device\n-      62177883-f1bb-47f0-914d-32a22e3a8804 into the guest named 'my-guest' with\n-      device-id hostdev0:\n-\n-      virsh qemu-monitor-command my-guest --hmp \\\n-      \"device_add vfio-ap,\\\n-      sysfsdev=/sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804,\\\n-      id=hostdev0\"\n-\n-2. A vfio_ap mediated device can be hot plugged by attaching the qemu monitor\n-   to the guest and using the following qemu monitor command:\n-\n-      (qemu) device_add \"vfio-ap,sysfsdev=\u003cpath-to-mdev\u003e,id=\u003cdevice-id\u003e\"\n-\n-      For example, to plug the vfio_ap mediated device\n-      62177883-f1bb-47f0-914d-32a22e3a8804 into the guest with the device-id\n-      hostdev0:\n+Live Guest Migration\n+====================\n+The VFIO AP mediated device is not used to provide userspace with direct\n+access to device memory as is the case with other devices that use the VFIO\n+framework for device pass-through. Instead, it manages AP\n+configuration metadata identifying the adapters, domains, and control\n+domains to which a guest will be granted access. These AP resources are\n+configured by assigning them to a vfio-ap mediated device via its sysfs\n+assignment interfaces. When the guest is started, the vfio_ap device driver\n+sets the guest's AP configuration from the metadata stored with the mediated\n+device. The AP devices are not accessed directly through the vfio_ap driver,\n+so the driver does not migrate the state of the AP devices themselves.\n+Instead, it migrates the AP configuration metadata. The vfio_ap device\n+driver on the destination host will then ensure that the AP configuration\n+metadata migrated from the source host is compatible with the AP\n+configuration available on the destination host and if\n+so, will set the destination guest's AP configuration accordingly.\n \n-         (QEMU) device-add \"vfio-ap,\\\n-         sysfsdev=/sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804,\\\n-         id=hostdev0\"\n+To be considered compatible, the AP configuration for the destination host must\n+meet these requirements:\n+\n+* Each AP adapter, domain and control domain number assigned to the source\n+  guest's AP configuration must be in the destination host's AP\n+  configuration. This can be verified as follows:\n+\n+  * On the source host, display the AP configuration of the source guest\n+    with the following two commands:\n+\n+    .. code-block:: bash\n+\n+       cat /sys/devices/vfio_ap/matrix/$UUID/guest_matrix\n+       cat /sys/devices/vfio_ap/matrix/$UUID/control_domains\n+\n+    .. note::\n+       Replace ``$UUID`` with the actual UUID of your mediated device.\n+\n+       The ``guest_matrix`` lists the APQNs of the queue devices assigned\n+       to the mediated device; for example:\n+\n+       .. code-block:: bash\n+\n+          00.0033\n+          00.003a\n+\n+       The ``control_domains`` lists the domain numbers of the control\n+       domains; for example:\n+\n+       .. code-block:: bash\n+\n+          0033\n+          003a\n+\n+  * Display the AP configuration of the destination host with the following\n+    two commands:\n+\n+    .. code-block:: bash\n+\n+       lszcrypt -V\n+       cat /sys/bus/ap/ap_control_domain_mask\n+\n+    .. note::\n+       The ``lszcrypt -V`` command lists the AP cards (adapters) and queue\n+       devices on the host system; for example:\n+\n+       .. code-block:: bash\n+\n+          =========== ===== ============ ==========\n+          CARD.DOMAIN TYPE  MODE         DRIVER\n+          =========== ===== ============ ==========\n+          00          CEX5C CCA-Coproc   cex4card\n+          00.0033     CEX5C CCA-Coproc   vfio_ap\n+          00.003a     CEX5C CCA-Coproc   vfio_ap\n+          =========== ===== ============ ==========\n+\n+       Each bit in the ap_control_domain_mask corresponds to\n+       domain numbers 0-255. If a bit is set to one, then\n+       the control domain is available on the source host. For\n+       example:\n+\n+       .. code-block:: bash\n+\n+          0x0000000000001020000000000000000000000000000000000000000000000000\n+\n+       This mask indicates that AP control domains 51 (0x33) and 58 (0x3a) are\n+       in the AP configuration of the host system.\n+\n+  * Verify the configurations:\n+\n+    * Each APQN in the guest's sysfs ``guest_matrix`` file must match an\n+      APQN returned from the ``lszcrypt -V`` command on the destination host. It\n+      is important to note the driver to which the queue is bound which\n+      is displayed in the ``driver`` column. Queues not bound to the vfio_ap\n+      driver are not available to the destination guest. Even if the APQNs are\n+      available to the driver, that does not guarantee they can be used by the\n+      guest being migrated; for example, migration will fail if any of these APQNs\n+      are assigned to a mediated device other than the one being migrated.\n+\n+    * Each domain number from the source guest's sysfs\n+      ``control_domains`` attribute must correspond to a bit set to one in\n+      the destination host's ``ap_control_domain_mask``.\n+\n+\n+* Each APQN assigned to the source guest and destination guest must\n+  reference a queue device with compatible hardware capabilities:\n+\n+  +--------------+---------------------------------------------------------------+\n+  | Hardware     | Description                                                   |\n+  | Capabilities |                                                               |\n+  +==============+===============================================================+\n+  | [1]          | * AP special command facility (APSC)                          |\n+  | Facilities   | * AP 4096-bit ME PKU commands facility (AP4KM)                |\n+  |              | * AP 4096-bit CRT PKU commands (AP4KC)                        |\n+  +--------------+---------------------------------------------------------------+\n+  | [1] Mode     | * CCA-mode                                                    |\n+  |              | * Accelerator-mode                                            |\n+  |              | * XCP-mode (EP11)                                             |\n+  +--------------+---------------------------------------------------------------+\n+  | [1] AP       |   APXA installed                                              |\n+  | extended     |                                                               |\n+  | addressing   |                                                               |\n+  +--------------+---------------------------------------------------------------+\n+  | [1] Command  |   Command filtering available                                 |\n+  | filtering    |                                                               |\n+  +--------------+---------------------------------------------------------------+\n+  | [2]          | * Full native card function                                   |\n+  | Functional   | * Only stateless functions                                    |\n+  | capabilities |                                                               |\n+  +--------------+---------------------------------------------------------------+\n+  | [3] AP type  | * 10 (0x0a): CEX4                                             |\n+  |              | * 11 (0x0b): CEX5                                             |\n+  |              | * 12 (0x0c)  CEX6                                             |\n+  |              | * 13 (0x0d)  CEX7                                             |\n+  |              | * 14 (0x0e)  CEX8                                             |\n+  +--------------+---------------------------------------------------------------+\n+\n+  +-----------------------------------------------------------------------------+\n+  | Table Keys                                                                  |\n+  +=============================================================================+\n+  |                                                                             |\n+  | [1]        The hardware capability must be the same for both the source     |\n+  |            and destination queue device.                                    |\n+  |                                                                             |\n+  | [2]        The source and destination queues must either both have          |\n+  |            full native card function or both have stateless functions.      |\n+  |            If the functional capabilities don't match, then the source      |\n+  |            queue can have stateless capabilities since full native card     |\n+  |            function can handle stateless functions; otherwise, migration    |\n+  |            will fail.                                                       |\n+  |                                                                             |\n+  | [3]        The AP type on the source and destination guests can             |\n+  |            differ if the queue passed through to the target guest is a      |\n+  |            newer model (backwards compatible)                               |\n+  +-----------------------------------------------------------------------------+\n+\n+  * To verify the hardware capabilities are compatible:\n+\n+    * On both the source and destination hosts, display the hardware capabilities\n+      for each AP adapter in the AP configuration for that host:\n+\n+      .. code-block:: bash\n+\n+         cat /sys/bus/ap/devices/card$APID/ap_functions\n+\n+      .. note::\n+         The ``$APID`` is the two-character adapter number in hexadecimal format;\n+         for example, ``card02`` or ``card1f``. The ``/sys/bus/ap/devices`` directory\n+         also lists the APQNs of the queue devices installed in the respective\n+         host system with the first two characters being the APID; for example:\n+         ``02.0004`` or ``00.0033`` .\n+\n+    * Verify the hardware capabilities for each AP adapter device on the source\n+      and destination host are compatible. The ``ap_functions`` attribute is a\n+      bitmask. The bits in the mask read from left to right starting with bit 0.\n+      Each bit that is set to one indicates the corresponding hardware capability\n+      is installed:\n+\n+      * Bits 0-2 are the facilities bits. Each bit value must match for the AP\n+        devices on the source and destination systems. The values indicate the\n+        following:\n+\n+        * bit-0: APSC is installed\n+        * bit-1: AP4KM is installed\n+        * bit-2: AP4KC is installed\n+\n+      * Bits 3-5 specify the adapter Modes. Only one of these bits will be set to\n+        one and must be the same for both the source and destination adapter\n+        devices:\n+\n+        * bit-3: CCA-mode\n+        * bit-4: Accelerator-mode\n+        * bit-5: XCP-mode (EP11)\n+\n+      * Bit 6 specifies whether APXA is installed and must be the same for both\n+        the source and destination adapter devices.\n+\n+      * Bit 7 specifies whether command filtering is installed\n+        and must be the same for both the source and destination adapter devices.\n+\n+      * Bits 8-15 are the classification bits. There are only two relevant\n+        bits and only one of them will be set to one. For the source and destination\n+        adapter devices to be considered compatible:\n+\n+        * Bit 8 may be set to 1 on both hosts (full native card function)\n+        * Bit 9 may be set to 1 on both hosts (only stateless functions available)\n+        * Bit 9 may be set to 1 on the source host and bit 8 may be set to 1 on\n+          the destination host\n+\n+      * Bits 16-17 are the binding state bits which must be 00\n+\n+  * To verify the AP types are compatible:\n+\n+    * On both the source and destination hosts, display the hardware type\n+      for each AP adapter in the AP configuration for that host to ensure the\n+      source and destination adapter types are the same, or the source type is\n+      less than the destination type::\n+\n+       cat /sys/bus/ap/devices/card$APID/hwtype\n+\n+      .. note::\n+         The ``$APID`` is the two-character adapter number in hexadecimal format;\n+         for example, ``card02`` or ``card1f``.\n+\n+Setting up for live guest migration on the destination host\n+-----------------------------------------------------------\n+To set up a host as the destination for live guest migration, do the\n+following:\n+\n+1. Create a mediated device\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+Create a mediated device with the same name (UUID) as the mediated device\n+used to supply the AP configuration to the source guest. For example, if\n+the mediated device on the source guest is named\n+62177883-f1bb-47f0-914d-32a22e3a8804:\n+\n+.. code-block:: bash\n+\n+   echo 62177883-f1bb-47f0-914d-32a22e3a8804 \u003e \\\n+     /sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/create\n+\n+2. Reserve adapters and domains for the vfio_ap device driver's use\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+Reserve the same adapters, and domains passed through to the source guest\n+for the vfio_ap device driver on the destination host by clearing the\n+adapter numbers from the ``/sys/bus/ap/apmask`` and/or the domain numbers\n+from the ``/sys/bus/ap/aqmask`` files.\n+\n+For example, suppose adapters 0, 20 (0x14) and 22 (0x16) as well as\n+domains 51 (0x33) and 58 (0x3a) are passed through to the source guest. To\n+ensure they can be passed through to the destination guest, they must be\n+made available to the vfio_ap device driver on the destination host. To\n+make them available, do one or both of the following on the destination host:\n+\n+.. code-block:: bash\n+\n+   echo -0,-0x14,-22 \u003e /sys/bus/ap/apmask\n+   echo -0,-0x33,-0x3a \u003e /sys/bus/ap/aqmask\n+\n+.. note::\n+\n+    * It is not necessary to reserve both the adapters and domains.\n+      Reserving an adapter implicitly allows the vfio_ap driver to assign\n+      each of the domains available on the system along with the reserved\n+      adapters to a guest's AP configuration and vice versa.\n+\n+    * The Cartesian product of the adapters and domains reserved for the\n+      vfio_ap device driver identifies the APQNs of the AP queue devices to be\n+      bound to the vfio_ap device driver. If an APQN does not reference a\n+      queue device installed in the system at the time of live guest migration,\n+      the migration will fail.\n+\n+    * It is not necessary to configure the destination mediated device with\n+      adapters, domains and control domains because the source guest's AP\n+      configuration will overlay the destination guest's during migration.\n+\n+    * To verify that the queue devices are reserved for the vfio_ap\n+      device driver, issue the ``lszcrypt -V`` command and you should\n+      see ``vfio_ap`` in the ``DRIVER`` column of the output.\n+\n+Live guest migration failures due to AP configuration errors\n+------------------------------------------------------------\n+This section describes the errors that may occur during live guest migration\n+when the AP configuration of the source guest and destination host are not\n+compatible and where to look for problem determination.\n+\n+The destination host is missing the mediated device\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+This error occurs when a mediated device with the same name (UUID) as the\n+mediated device being migrated has not been created on the destination host.\n+\n+**Source host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | error: device not found: mediated device '$UUID' not           |\n+|              | found                                                          |\n++--------------+----------------------------------------------------------------+\n+| QEMU log:    | initiating migration                                           |\n+|              | qemu-system-s390x: Sibling indicated error 1                   |\n++--------------+----------------------------------------------------------------+\n+\n+.. note::\n+   $UUID is the UUID of the mediated device being migrated from the source host.\n+\n+\n+One or more queues are not bound to the vfio_ap driver on the destination host\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+The queue devices passed through to the source guest are not installed in the\n+destination host or are not bound to its vfio_ap device driver.\n+\n+**Source host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | error: operation failed: migration failed. Message from the    |\n+|              | source host: operation failed: job 'migration out' failed:     |\n+|              | Sibling indicated error 1. Message from the destination host:  |\n+|              | operation failed: job 'migration in' failed: load of migration |\n+|              | failed: No such device: Failed to load vmstate version_id: 1,  |\n+|              | ret: -19                                                       |\n++--------------+----------------------------------------------------------------+\n+\n+**Destination host**\n+\n++--------------+----------------------------------------------------------------+\n+| Kernel log:  | vfio_ap_mdev $UUID: Queue $APID.$APQI not available to         |\n+|              | vfio_ap driver on target host                                  |\n++--------------+----------------------------------------------------------------+\n+| QEMU log:    | shutting down, reason=failed                                   |\n+|              | qemu-system-s390x: terminating on signal $SIGNAL_NUM from      |\n+|              | pid $PID $DAEMON                                               |\n++--------------+----------------------------------------------------------------+\n+\n+\n+One or more control domains are not available on the destination host\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+The control domains passed through to the source guest are not installed on the\n+destination host.\n+\n+**Source host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | error: operation failed: migration failed. Message from the    |\n+|              | source host: operation failed: job 'migration out' failed:     |\n+|              | Sibling indicated error 1. Message from the destination host:  |\n+|              | operation failed: job 'migration in' failed: load of migration |\n+|              | failed: No such device: Failed to load vmstate version_id: 1,  |\n+|              | ret: -19                                                       |\n++--------------+----------------------------------------------------------------+\n+\n+**Destination host**\n+\n++--------------+----------------------------------------------------------------+\n+| Kernel log:  | vfio_ap_mdev $UUID: Control domain $DOMAIN_NUM not available   |\n+|              | on the destination host                                        |\n++--------------+----------------------------------------------------------------+\n+| QEMU log:    | shutting down, reason=failed                                   |\n+|              | qemu-system-s390x: terminating on signal $SIGNAL_NUM from      |\n+|              | pid $PID $DAEMON                                               |\n++--------------+----------------------------------------------------------------+\n+\n+\n+.. note::\n+   * $UUID is the UUID of the mediated device being migrated from the source host.\n+   * $APID.$APQI is adapter number and queue index (domain number) comprising the\n+     $APQN (for example, 00.0033) of the queue device that is not available.\n+   * $SIGNAL_NUM is the signal number that caused the QEMU process to terminate.\n+   * $PID is the process ID of the QEMU process on the destination host.\n+   * $DAEMON is the name of the QEMU daemon process.\n+   * This message will be logged for each queue for which this error is detected.\n+\n+Failure to get hardware information for a queue device on destination host\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+The vfio_ap device driver on the destination host will execute an AP instruction\n+to get the hardware information for a queue device to perform queue compatibility\n+checks. The instruction can fail for a number of reasons in which case the\n+migration will be terminated.\n+\n+**Source host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | error: operation failed: migration failed. Message from the    |\n+|              | source host: operation failed: job 'migration out' failed:     |\n+|              | Sibling indicated error 1. Message from the destination host:  |\n+|              | operation failed: job 'migration in' failed: load of migration |\n+|              | failed: failed to get hardware information for queue device    |\n+|              | $APQN                                                          |\n++--------------+----------------------------------------------------------------+\n+\n+**Destination host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | vfio_ap_mdev $UUID: migration failed: Failed to get hwinfo for |\n+|              | queue $APQN on target host: rc=$RESPONSE-CODE                  |\n++--------------+----------------------------------------------------------------+\n+| QEMU log:    | shutting down, reason=failed                                   |\n+|              | qemu-system-s390x: terminating on signal $SIGNAL-ID from pid   |\n+|              | $PID $DAEMON                                                   |\n++--------------+----------------------------------------------------------------+\n+\n+.. note::\n+   * $UUID is the UUID of the mediated device being migrated from the source host.\n+   * $APQN (for example, 00.0033) of the queue device that is not compatible.\n+   * $RESPONSE_CODE is the response code from the AP instruction.\n+   * $SIGNAL_NUM is the signal number of the signal that caused the QEMU process\n+     to terminate.\n+   * $PID is the process ID of the QEMU process on the destination host.\n+   * $DAEMON is the name of the QEMU daemon process.\n+   * This message will be logged for each queue for which this error is detected.\n+\n+The AP configurations of the source guest and destination host not compatible\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+The functional capabilities or facilities available on one or more of the queues\n+passed through to the source guest are not compatible with the queue device with\n+the same APQN on the destination system (see the Hardware Capabilities table above)\n+\n+**Source host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | error: operation failed: migration failed. Message from the    |\n+|              | source host: operation failed: job 'migration out' failed:     |\n+|              | Sibling indicated error 1. Message from the destination host:  |\n+|              | operation failed: job 'migration in' failed: load of migration |\n+|              | failed: No such device: Failed to load vmstate version_id: 1,  |\n+|              | ret: -19                                                       |\n++--------------+----------------------------------------------------------------+\n+| Kernel log:  | N/A                                                            |\n++--------------+----------------------------------------------------------------+\n+| QEMU log:    | *initiating migration*                                         |\n+|              |                                                                |\n+|              | *qemu-system-s390x: Sibling indicated error 1*                 |\n++--------------+----------------------------------------------------------------+\n+\n+**Destination host**\n+\n++--------------+----------------------------------------------------------------+\n+| Log          | Message                                                        |\n++==============+================================================================+\n+| Console log: | N/A                                                            |\n++--------------+----------------------------------------------------------------+\n+| Kernel log:  | *vfio_ap_mdev $UUID: Migration failed: Source and target       |\n+|              | queue ($APQN) not compatible*                                  |\n+|              |                                                                |\n+|              | The message above will be followed by one or more messages     |\n+|              | enumerating the incompatible features; for example:            |\n+|              |                                                                |\n+|              | ``vfio_ap_mdev $UUID: APSC facility installed in source queue  |\n+|              | $APQN``                                                        |\n+|              | ``vfio_ap_mdev $UUID: APSC facility not installed in target    |\n+|              | queue $APQN``                                                  |\n+|              |                                                                |\n+|              | ``AP type of source ($APTYPE1) not compatible with target      |\n+|              | ($APTYPE2)`` ...                                               |\n++--------------+----------------------------------------------------------------+\n+| QEMU log:    | *initiating migration*                                         |\n+|              |                                                                |\n+|              | *qemu-system-s390x: error while loading state section id ...*  |\n+|              |                                                                |\n+|              | *shutting down, reason=failed*                                 |\n+|              |                                                                |\n+|              | *terminating on signal $SIGNAL-ID from pid $PID $DAEMON*       |\n++--------------+----------------------------------------------------------------+\n+\n+.. note::\n+   * $UUID is the UUID of the mediated device being migrated from the source host.\n+   * $APQN (for example, 00.0033) of the queue device that is incompatible.\n+   * $APTYPE1 is the type of the source queue device.\n+   * $APTYPE2 is the type of the target queue device.\n+   * $SIGNAL-ID is the signal number of the signal that caused the QEMU process\n+     to terminate.\n+   * $PROCESS-ID is the process ID of the QEMU process on the source host.\n+   * $PID is the process ID of the QEMU process on the destination host.\n+   * $DAEMON is the name of the QEMU daemon process.\n+   * Each follow-on message logging the incompatibility will be logged for each\n+     error detected for the specified queue device.\n+\n+AP Configuration Management\n+---------------------------\n+The AP configurations of the source guest and destination host must be kept\n+in synchronization or live guest migration will likely fail due to potential\n+incompatibilities introduced by such changes. In particular, changing adapter\n+or domain reservations via the sysfs ``apmask`` or ``aqmask`` attributes on\n+either the source or destination host without coordinating such changes between\n+the two hosts -especially while a migration is taking place - can result in\n+migration failures.\n+\n+Configuration stability is an orchestration-layer or system administrator\n+responsibility, which is consistent with other VFIO device types. The vfio_ap\n+device driver will validate source and destination configurations and provide\n+clear diagnostics when incompatibilities are detected, enabling orchestration\n+tools to implement appropriate policies; for example, blocking migration when\n+such changes are to be made.\n+\n+Master Key administration\n+--------------------------\n+It is imperative that s390 Common Cryptographic Architecture (CCA) master key\n+administration be performed on both the source and destination AP\n+devices to synchronize the key values prior to allowing live guest\n+migration. If the master keys do not match, then crypto applications that\n+rely on secure keys wrapped by a CCA master key will fail when the guest on\n+which they are running is migrated to the destination host. Again, this is\n+an orchestration-layer or system administrator responsibility and migration must\n+be blocked while master key administration is being performed.\ndiff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile\nindex e83c6603c8587d..20f29184825a7b 100644\n--- a/drivers/s390/crypto/Makefile\n+++ b/drivers/s390/crypto/Makefile\n@@ -34,5 +34,5 @@ pkey-uv-objs := pkey_uv.o\n obj-$(CONFIG_PKEY_UV) += pkey-uv.o\n \n # adjunct processor matrix\n-vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o\n+vfio_ap-objs := vfio_ap_drv.o vfio_ap_ops.o vfio_ap_migration.o\n obj-$(CONFIG_VFIO_AP) += vfio_ap.o\ndiff --git a/drivers/s390/crypto/vfio_ap_drv.c b/drivers/s390/crypto/vfio_ap_drv.c\nindex 8e69ed286bb9ca..4e973736522bc1 100644\n--- a/drivers/s390/crypto/vfio_ap_drv.c\n+++ b/drivers/s390/crypto/vfio_ap_drv.c\n@@ -26,9 +26,11 @@ MODULE_LICENSE(\"GPL v2\");\n struct ap_matrix_dev *matrix_dev;\n debug_info_t *vfio_ap_dbf_info;\n \n+#define FEATURES \"guest_matrix hotplug ap_config migratable\"\n+\n static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)\n {\n-\treturn sysfs_emit(buf, \"guest_matrix hotplug ap_config\\n\");\n+\treturn sysfs_emit(buf, \"%s\\n\", FEATURES);\n }\n static DEVICE_ATTR_RO(features);\n \ndiff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c\nnew file mode 100644\nindex 00000000000000..20b519f0898f5d\n--- /dev/null\n+++ b/drivers/s390/crypto/vfio_ap_migration.c\n@@ -0,0 +1,1560 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Drives vfio_ap mdev migration.\n+ *\n+ * Copyright IBM Corp. 2025\n+ */\n+#include \u003clinux/anon_inodes.h\u003e\n+#include \u003clinux/file.h\u003e\n+#include \"vfio_ap_private.h\"\n+\n+/*\n+ * Masks the fields of the queue information returned from the PQAP(TAPQ)\n+ * command. In order to migrate a guest, it's AP configuration must be\n+ * compatible with AP configuration assigned to the target guest's mdev.\n+ * This mask is used to verify that the queue information for each source and\n+ * target queue is compatible.\n+ *\n+ * The following bits must match for the source device and the corresponding\n+ * destination device:\n+ * -------------------------------------------------------------------------\n+ * S bit 0: APSC  facility installed\n+ * M bit 1: APQKM facility installed\n+ * C bit 2: AP4KC facility installed\n+ * Mode bits 3-5:\n+ *     D bit 3: CCA-mode facility\n+ *     A bit 4: accelerator-mode facility\n+ *     X bit 5: XCP-mode facility\n+ * N  bit 6: APXA facility installed\n+ * SL bit 7: SLCF facility installed\n+ *\n+ * Either bit 8 or bit 9 will be set. If bit 8 is set for the source device,\n+ * then it must also be set for the corresponding destination device:\n+ * -------------------------------------------------------------------------\n+ * Classification (functional capabilities) bits 8-16\n+ *     bit 8: Native card function\n+ *     bit 9: Only stateless functions\n+ *\n+ * The BS bits must be set to 0 for both the source and corresponding\n+ * destination device:\n+ * -------------------------------------------------------------------------\n+ * BS bits 16-17:\n+ *\n+ * The AP type of the source device must be less than or equal to that of\n+ * the corresponding destination device:\n+ * -------------------------------------------------------------------------\n+ * AP Type bits 32-40:\n+ */\n+#define QINFO_DATA_MASK\t\t0xffffc000ff000000\n+\n+/*\n+ * Masks the bit that indicates whether full native card function is available\n+ * from the 8 bits specifying the functional capabilities of a queue\n+ */\n+#define CLASSIFICATION_NATIVE_FCN_MASK\t\t0x80\n+\n+/* The maximum number of queues that can be installed in an s390 system */\n+#define MAX_AP_QUEUES\t\t\t\t(AP_DEVICES * AP_DOMAINS)\n+\n+/**\n+ * vfio_ap_migration_file\n+ *\n+ * This object is used for chunk processing of multiple reads and writes of\n+ * AP configuration information.\n+ *\n+ * @filp:\tfile stream used to read or write AP configuration data\n+ * @ap_config:\tobject used to store AP configuration data between read or write\n+ *\t\tcalls\n+ * @config_sz:\tthe size (in bytes) of @ap_config\n+ */\n+struct vfio_ap_migration_file {\n+\tstruct file\t\t*filp;\n+\tstruct vfio_ap_config\t*ap_config;\n+\tunsigned long\t\tconfig_sz;\n+};\n+\n+/**\n+ * vfio_ap_migration_data\n+ *\n+ * Manages the migration state for the VFIO device that maintains the AP\n+ * configuration of the guest being migrated.\n+ *\n+ * @mig_state:\t\tthe current migration state\n+ * @resuming_mig_file:\tthe object used to restore the state of the vfio-ap\n+ *\t\t\tdevice the destination host:\n+ * @stop_copy_mig_file: the object used to store the AP configuration of the\n+ *\t\t\tsource guest for transfer to the destination host.\n+ */\n+struct vfio_ap_migration_data {\n+\tenum vfio_device_mig_state\tmig_state;\n+\tstruct vfio_ap_migration_file\tresuming_mig_file;\n+\tstruct vfio_ap_migration_file\tstop_copy_mig_file;\n+};\n+\n+/**\n+ * vfio_ap_queue_info - the information for an AP queue\n+ *\n+ * @data: contains the queue information returned in GR2 from the PQAP(TAPQ)\n+ *\t  command\n+ * @apqn: the APQN of the queue\n+ * @reserved: padding to ensure consistent structure size across platforms\n+ */\n+struct vfio_ap_queue_info {\n+\tu64 data;\n+\tu16 apqn;\n+\tu8  reserved[6];\n+};\n+\n+/**\n+ * vfio_ap_config - the guest's AP configuration\n+ *\n+ * @num_queues:\tthe number of queues passed through to the guest\n+ * @reserved:\tpadding to ensure proper alignment of @cntl_domains\n+ * @adm:\tbitmap specifying the control domains in the AP configuration\n+ * @qinfo:\tan array of vfio_ap_queue_info objects, each specifying the\n+ *\t\tqueue information for a queue passed through to the guest\n+ */\n+struct vfio_ap_config {\n+\tu32\t\t\t\tnum_queues;\n+\tu8\t\t\t\treserved[4];\n+\tu64\t\t\t\tadm[DIV_ROUND_UP(AP_DOMAINS, 64)];\n+\tstruct vfio_ap_queue_info\tqinfo[] __counted_by(num_queues);\n+};\n+\n+static void\n+vfio_ap_release_stop_copy_file(struct vfio_ap_migration_data *mig_data)\n+{\n+\tkfree(mig_data-\u003estop_copy_mig_file.ap_config);\n+\tmig_data-\u003estop_copy_mig_file.ap_config = NULL;\n+\tmig_data-\u003estop_copy_mig_file.config_sz = 0;\n+\tmig_data-\u003estop_copy_mig_file.filp = NULL;\n+}\n+\n+static void\n+vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_data)\n+{\n+\tkfree(mig_data-\u003eresuming_mig_file.ap_config);\n+\tmig_data-\u003eresuming_mig_file.ap_config = NULL;\n+\tmig_data-\u003eresuming_mig_file.config_sz = 0;\n+\tmig_data-\u003eresuming_mig_file.filp = NULL;\n+}\n+\n+static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tint ret = 0;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tmatrix_mdev = filp-\u003eprivate_data;\n+\tif (!matrix_mdev || !matrix_mdev-\u003emig_data) {\n+\t\tret = -ENODEV;\n+\t\tgoto done;\n+\t}\n+\n+\tif (filp == matrix_mdev-\u003emig_data-\u003estop_copy_mig_file.filp)\n+\t\tvfio_ap_release_stop_copy_file(matrix_mdev-\u003emig_data);\n+\telse if (filp == matrix_mdev-\u003emig_data-\u003eresuming_mig_file.filp)\n+\t\tvfio_ap_release_resuming_file(matrix_mdev-\u003emig_data);\n+\telse\n+\t\tret = -ENOENT;\n+\n+done:\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\treturn ret;\n+}\n+\n+/**\n+ * validate_stop_copy_read_parms: Validate the input parameters to the\n+ *                                vfio_ap_stop_copy_read function\n+ *\n+ * @matrix_mdev: The object device containing the state to be read\n+ * @filp: Pointer to the file stream used to read the vfio-ap device state\n+ * @pos:  The file offset from which to start reading data\n+ * @len:  The length of the data to be read\n+ *\n+ * Verify the following:\n+ * - @filp private data is an ap_matrix_mdev instance\n+ * - @filp is the instance opened when state transitioned from STOP to STOP_COPY\n+ * - @pos + @len does not cause integer overflow\n+ *\n+ * Returns: 0 if the parameters pass validation; otherwise returns an error\n+ */\n+static int validate_stop_copy_read_parms(struct file *filp, loff_t *pos,\n+\t\t\t\t\t size_t len)\n+{\n+\tstruct vfio_ap_migration_data *mig_data;\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tloff_t total_len;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tif (check_add_overflow((loff_t)len, *pos, \u0026total_len))\n+\t\treturn -EIO;\n+\n+\tmatrix_mdev = filp-\u003eprivate_data;\n+\n+\tif (!matrix_mdev || !matrix_mdev-\u003emig_data)\n+\t\treturn -ENODEV;\n+\n+\tmig_data = matrix_mdev-\u003emig_data;\n+\n+\tif (mig_data-\u003estop_copy_mig_file.filp != filp)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static size_t vfio_ap_config_size(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t\t  int *num_queues)\n+{\n+\tsize_t qinfo_size;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\t*num_queues = vfio_ap_mdev_get_num_queues(\u0026matrix_mdev-\u003eshadow_apcb);\n+\tqinfo_size = *num_queues * sizeof(struct vfio_ap_queue_info);\n+\n+\treturn qinfo_size + sizeof(struct vfio_ap_config);\n+}\n+\n+static int get_hardware_info_for_queue(const char *mdev_name,\n+\t\t\t\t       struct ap_tapq_hwinfo *hwinfo,\n+\t\t\t\t       unsigned long apqn)\n+{\n+\tstruct ap_queue_status status;\n+\n+\tstatus = ap_tapq(apqn, hwinfo);\n+\n+\tswitch (status.response_code) {\n+\tcase AP_RESPONSE_NORMAL:\n+\tcase AP_RESPONSE_RESET_IN_PROGRESS:\n+\tcase AP_RESPONSE_DECONFIGURED:\n+\tcase AP_RESPONSE_CHECKSTOPPED:\n+\tcase AP_RESPONSE_BUSY:\n+\t\t/* For all these RCs the tapq info should be available */\n+\t\treturn 0;\n+\tcase AP_RESPONSE_Q_NOT_AVAIL:\n+\t\tpr_err(\"vfio_ap_mdev %s: Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d\",\n+\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),\n+\t\t       status.response_code);\n+\t\treturn -ENODEV;\n+\tdefault:\n+\t\t/*\n+\t\t * Without a pending async error, the tapq info should be\n+\t\t * available\n+\t\t */\n+\t\tif (status.async)\n+\t\t\treturn 0;\n+\n+\t\tpr_err(\"vfio_ap_mdev %s:Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d\",\n+\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),\n+\t\t       status.response_code);\n+\t\treturn -EIO;\n+\t}\n+}\n+\n+static int vfio_ap_store_queue_info(const char *mdev_name,\n+\t\t\t\t    struct vfio_ap_config *ap_config)\n+{\n+\tstruct ap_tapq_hwinfo source_hwinfo;\n+\tunsigned long num_queues;\n+\tint ret;\n+\n+\t/*\n+\t * ap_tapq() is a hardware instruction that may take time to complete.\n+\t * It must be called without mdevs_lock held to avoid blocking other\n+\t * mdevs. The apqn list was already snapshotted into ap_config-\u003eqinfo[]\n+\t * by the caller under the lock.\n+\t */\n+\tfor (num_queues = 0; num_queues \u003c ap_config-\u003enum_queues; num_queues++) {\n+\t\tret = get_hardware_info_for_queue(mdev_name, \u0026source_hwinfo,\n+\t\t\t\t\t\t  ap_config-\u003eqinfo[num_queues].apqn);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tap_config-\u003eqinfo[num_queues].data = source_hwinfo.value;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+vfio_ap_get_config(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tunsigned long *apm, *aqm, apid, apqi, num_queues;\n+\tstruct vfio_ap_config *ap_configuration;\n+\tconst char *mdev_name;\n+\tsize_t ap_config_size;\n+\tint ret;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tap_config_size = vfio_ap_config_size(matrix_mdev, (int *)\u0026num_queues);\n+\n+\tap_configuration = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);\n+\tif (!ap_configuration)\n+\t\treturn -ENOMEM;\n+\n+\t/*\n+\t * Snapshot the APQN list from shadow_apcb under the lock so that\n+\t * ap_tapq() calls in vfio_ap_store_queue_info() can happen without it.\n+\t */\n+\tapm = matrix_mdev-\u003eshadow_apcb.apm;\n+\taqm = matrix_mdev-\u003eshadow_apcb.aqm;\n+\tnum_queues = 0;\n+\tfor_each_set_bit_inv(apid, apm, AP_DEVICES) {\n+\t\tfor_each_set_bit_inv(apqi, aqm, AP_DOMAINS) {\n+\t\t\tap_configuration-\u003eqinfo[num_queues].apqn =\n+\t\t\t\tAP_MKQID(apid, apqi);\n+\t\t\tnum_queues += 1;\n+\t\t}\n+\t}\n+\tap_configuration-\u003enum_queues = num_queues;\n+\tmemcpy(ap_configuration-\u003eadm, matrix_mdev-\u003eshadow_apcb.adm,\n+\t       sizeof(ap_configuration-\u003eadm));\n+\tmdev_name = dev_name(matrix_mdev-\u003evdev.dev);\n+\n+\t/**\n+\t * Release the global mdevs_lock which guards access to all mdevs.\n+\t * Storing the queue information could take a while if there are a\n+\t * large number of queues.\n+\t */\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tret = vfio_ap_store_queue_info(mdev_name, ap_configuration);\n+\tif (ret) {\n+\t\tkfree(ap_configuration);\n+\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn ret;\n+\t}\n+\n+\t/* Retake the mdevs lock so we can safely make updates to the mdev */\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\t/*\n+\t * Verify mig_data is still valid - mdevs_lock was dropped, so the\n+\t * device could have been closed concurrently.\n+\t */\n+\tif (!matrix_mdev-\u003emig_data) {\n+\t\tkfree(ap_configuration);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tmatrix_mdev-\u003emig_data-\u003estop_copy_mig_file.ap_config = ap_configuration;\n+\tmatrix_mdev-\u003emig_data-\u003estop_copy_mig_file.config_sz = ap_config_size;\n+\n+\treturn 0;\n+}\n+\n+static ssize_t vfio_ap_stop_copy_read(struct file *filp, char __user *buf,\n+\t\t\t\t      size_t len, loff_t *pos)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tstruct vfio_ap_config *ap_config;\n+\tssize_t ret = 0;\n+\tsize_t ap_config_size;\n+\n+\t/*\n+\t * When userspace calls read() with an explicit offset (pread), pos is\n+\t * non-NULL and the function rejects it with -ESPIPE (illegal seek). For\n+\t * normal read() calls, pos is NULL, so we'll use the file's internal\n+\t * position filp-\u003ef_pos\n+\t */\n+\tif (pos)\n+\t\treturn -ESPIPE;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tpos = \u0026filp-\u003ef_pos;\n+\n+\tret = validate_stop_copy_read_parms(filp, pos, len);\n+\tif (ret) {\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn ret;\n+\t}\n+\n+\tmatrix_mdev = filp-\u003eprivate_data;\n+\tif (!matrix_mdev-\u003emig_data-\u003estop_copy_mig_file.ap_config) {\n+\t\tret = vfio_ap_get_config(matrix_mdev);\n+\t\tif (ret) {\n+\t\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tap_config_size = matrix_mdev-\u003emig_data-\u003estop_copy_mig_file.config_sz;\n+\n+\t/*\n+\t * If the position exceeds the size of the AP configuration data,\n+\t * then indicate EOF; otherwise calculate the length of the data to\n+\t * read such that a buffer overrun is prevented.\n+\t */\n+\tif (*pos \u003e= ap_config_size)\n+\t\tlen = 0;\n+\telse\n+\t\tlen = min_t(size_t, ap_config_size - *pos, len);\n+\n+\t/* If we've reached an EOF condition, let the caller know */\n+\tif (len == 0) {\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Allocate ap_config and copy the content of the object caching the\n+\t * AP configuration data between calls to it so we can give up the\n+\t * global mdevs_lock while copying the data to userspace.\n+\t */\n+\tap_config = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);\n+\tif (!ap_config) {\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tmemcpy(ap_config, matrix_mdev-\u003emig_data-\u003estop_copy_mig_file.ap_config,\n+\t       ap_config_size);\n+\n+\t/*\n+\t * Give up the global mdevs_lock while copying data to the user; it\n+\t * might be a long running operation and we don't want to prevent access\n+\t * to other mdevs for an inordinate amount of time.\n+\t */\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tif (copy_to_user(buf, (char *)ap_config + *pos, len)) {\n+\t\tkfree(ap_config);\n+\t\treturn -EFAULT;\n+\t}\n+\n+\tkfree(ap_config);\n+\t*pos += len;\n+\n+\treturn len;\n+}\n+\n+static const struct file_operations vfio_ap_stop_copy_fops = {\n+\t.owner = THIS_MODULE,\n+\t.read = vfio_ap_stop_copy_read,\n+\t.compat_ioctl = compat_ptr_ioctl,\n+\t.release = vfio_ap_release_mig_file,\n+};\n+\n+static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t\t\t     const struct file_operations *fops,\n+\t\t\t\t\t     int flags)\n+{\n+\tstruct file *filp;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tfilp = anon_inode_getfile(\"vfio_ap_mig_file\", fops, matrix_mdev, flags);\n+\tif (!IS_ERR(filp))\n+\t\tstream_open(filp-\u003ef_inode, filp);\n+\n+\treturn filp;\n+}\n+\n+static int validate_resuming_write_parms(struct file *filp,\n+\t\t\t\t\t size_t len, loff_t *pos)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tloff_t total_len;\n+\tint ret;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tmatrix_mdev = filp-\u003eprivate_data;\n+\tif (!matrix_mdev || !matrix_mdev-\u003emig_data) {\n+\t\tret = -ENODEV;\n+\t\tgoto done;\n+\t}\n+\n+\tif (filp != matrix_mdev-\u003emig_data-\u003eresuming_mig_file.filp) {\n+\t\tret = -ENXIO;\n+\t\tgoto done;\n+\t}\n+\n+\tif (*pos \u003c 0) {\n+\t\tret = -EINVAL;\n+\t\tgoto done;\n+\t}\n+\n+\tif (check_add_overflow((loff_t)len, *pos, \u0026total_len)) {\n+\t\tret = -ERANGE;\n+\t\tgoto done;\n+\t}\n+\n+\t/*\n+\t * If the ap_config has not yet been allocated and the file position\n+\t * indicates this is not the first write, or the ap_config has been allocated\n+\t * but the file position indicates this is the first write, then this is an\n+\t * error condition.\n+\t */\n+\tif ((!matrix_mdev-\u003emig_data-\u003eresuming_mig_file.ap_config \u0026\u0026 *pos != 0) ||\n+\t    (matrix_mdev-\u003emig_data-\u003eresuming_mig_file.ap_config \u0026\u0026 *pos == 0)) {\n+\t\tret = -EFAULT;\n+\n+\t\tgoto done;\n+\t}\n+\n+\tret = 0;\n+\n+done:\n+\treturn ret;\n+}\n+\n+static ssize_t calculate_ap_config_size(unsigned int num_queues)\n+{\n+\tsize_t qinfo_size;\n+\n+\tif (num_queues \u003e MAX_AP_QUEUES)\n+\t\treturn -EINVAL;\n+\n+\tqinfo_size = num_queues * sizeof(struct vfio_ap_queue_info);\n+\treturn qinfo_size + sizeof(struct vfio_ap_config);\n+}\n+\n+/**\n+ * allocate_ap_config:\n+ *\n+ * Allocate storage for the source guest's AP configuration data sent from\n+ * userspace.\n+ *\n+ * @ap_config:\tThe location in which to store the pointer to the storage\n+ *\t\tallocated for the AP configuration data.\n+ * @buf:\tThe userspace buffer containing some or all of the source\n+ *\t\tguest's AP configuration data\n+ * @len:\tThe number of bytes of data to copy from @buf\n+ *\n+ * Returns:\tThe number of bytes of storage allocated for the config data or\n+ *\t\tan error:\n+ *\n+ *\n+ *\t\t-EIO: failed to copy data from @buf\n+ *\t\t-EINVAL: the number of queues specified exceeds the max allowed\n+ *\t\t-ENOMEM: the allocation of storage failed\n+ */\n+static ssize_t allocate_ap_config(struct vfio_ap_config **ap_config,\n+\t\t\t\t  const char __user *buf, size_t len)\n+{\n+\tstruct vfio_ap_config tmp_ap_config;\n+\tssize_t config_size;\n+\tsize_t copy_size;\n+\n+\t/*\n+\t * If the length of the data sent exceeds the size of the vfio_ap_config\n+\t * structure, then we will copy enough data from userspace to get the\n+\t * number of queues which we can use to allocate enough space all of\n+\t * the queue information.\n+\t */\n+\tcopy_size = min(len, sizeof(tmp_ap_config));\n+\n+\tif (copy_from_user(\u0026tmp_ap_config, buf, copy_size))\n+\t\treturn -EIO;\n+\n+\t/*\n+\t * If the length of data sent includes the number of queues\n+\t * in the AP configuration, then calculate its size; otherwise\n+\t * set config_size to the length of data sent.\n+\t */\n+\tif (len \u003e= sizeof(struct vfio_ap_config)) {\n+\t\tconfig_size = calculate_ap_config_size(tmp_ap_config.num_queues);\n+\n+\t\t/* If the calculation returned an error */\n+\t\tif (config_size \u003c 0)\n+\t\t\treturn config_size;\n+\t} else {\n+\t\tconfig_size = len;\n+\t}\n+\n+\t*ap_config = kzalloc(config_size, GFP_KERNEL_ACCOUNT);\n+\tif (!*ap_config)\n+\t\treturn -ENOMEM;\n+\n+\treturn config_size;\n+}\n+\n+static ssize_t reallocate_ap_config(struct vfio_ap_migration_file *mig_file,\n+\t\t\t\t    struct vfio_ap_config **new_ap_config,\n+\t\t\t\t    size_t len)\n+{\n+\tstruct vfio_ap_config *cur_ap_config;\n+\tunsigned long cur_cfg_sz;\n+\tssize_t new_cfg_sz;\n+\n+\tcur_ap_config = mig_file-\u003eap_config;\n+\tcur_cfg_sz = mig_file-\u003econfig_sz;\n+\n+\t/*\n+\t * If the current configuration size is greater than the\n+\t * size of a vfio_ap_config structure (i.e., contains the num_queues\n+\t * field), then there should already be enough storage allocated\n+\t * to store the source guest's AP configuration. Let's verify that the\n+\t * amount of storage allocated is what we expect based on the number of\n+\t * vfio_ap_queue_info objects that must be stored.\n+\t */\n+\tif (cur_cfg_sz \u003e= sizeof(struct vfio_ap_config)) {\n+\t\tnew_cfg_sz = calculate_ap_config_size(cur_ap_config-\u003enum_queues);\n+\t\tif (cur_cfg_sz != new_cfg_sz)\n+\t\t\treturn -EINVAL;\n+\t} else {\n+\t\tif (check_add_overflow(cur_cfg_sz, len, \u0026new_cfg_sz))\n+\t\t\treturn -ERANGE;\n+\t}\n+\n+\t*new_ap_config = kzalloc(new_cfg_sz, GFP_KERNEL_ACCOUNT);\n+\tif (!*new_ap_config)\n+\t\treturn -ENOMEM;\n+\n+\tmemcpy(*new_ap_config, cur_ap_config, cur_cfg_sz);\n+\n+\treturn new_cfg_sz;\n+}\n+\n+/**\n+ * qdev_is_bound_to_vfio_ap:\n+ *\n+ * Query to determine whether a queue with the specified APQN is available on\n+ * the host system and bound to the vfio_ap device driver.\n+ *\n+ * @apqn: The APQN of the queue device being queried\n+ *\n+ * Returns: True if there is a queue device with the specified @apqn installed\n+ *\t    in the system and is bound to the vfio_ap device driver; otherwise,\n+ *\t    returns false.\n+ */\n+static bool qdev_is_bound_to_vfio_ap(unsigned int apqn)\n+{\n+\tstruct ap_queue *queue;\n+\tbool is_bound = true;\n+\n+\tqueue = ap_get_qdev(apqn);\n+\tif (!queue)\n+\t\treturn false;\n+\n+\tif (queue-\u003eap_dev.device.driver != \u0026matrix_dev-\u003evfio_ap_drv-\u003edriver)\n+\t\tis_bound = false;\n+\n+\tput_device(\u0026queue-\u003eap_dev.device);\n+\n+\treturn is_bound;\n+}\n+\n+/**\n+ * queues_available:\n+ *\n+ * Query whether each queue from the source guest's AP configuration is\n+ * available and bound to the vfio_ap device driver; if not, log an error\n+ * message.\n+ *\n+ * @mdev_name:\t   The mdev name to use in error messages\n+ * @source_config: The object specifying the source guest's AP configuration\n+ *\n+ * Returns: true if each queue identified in @source_config is available and\n+ *\t    bound to the vfio_ap device driver; otherwise, returns false.\n+ */\n+static bool queues_available(const char *mdev_name,\n+\t\t\t     struct vfio_ap_config *source_config)\n+{\n+\tunsigned long apqn;\n+\tbool ret = true;\n+\n+\tfor (int i = 0; i \u003c source_config-\u003enum_queues; i++) {\n+\t\tapqn = source_config-\u003eqinfo[i].apqn;\n+\n+\t\t/*\n+\t\t * Find the queue device bound to the vfio_ap device driver. If it is\n+\t\t * not found, log an error and continue so users see all problems\n+\t\t * at once, not one-at-a-time through retries of the migration.\n+\t\t */\n+\t\tif (!qdev_is_bound_to_vfio_ap(apqn)) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Queue %02lx.%04lx not available to vfio_ap driver on target host\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t\tret = false;\n+\t\t}\n+\t}\n+\n+\treturn ret;\n+}\n+\n+/**\n+ * control_domains_available\n+ *\n+ * Query whether each control domain specified in the source guest's AP\n+ * configuration is installed in the host system.\n+ *\n+ * @mdev_name:\t\tThe name of the mdev to use when logging messages\n+ * @source_config:\tThe object specifying the source guest's AP config\n+ *\n+ * Returns:\tTrue if each control domain is installed; otherwise, logs an\n+ *\t\terror message for each unavailable control domain and returns\n+ *\t\tfalse.\n+ */\n+static bool control_domains_available(const char *mdev_name,\n+\t\t\t\t      struct vfio_ap_config *source_config)\n+{\n+\tunsigned long domain_num;\n+\tbool available = true;\n+\n+\tfor_each_set_bit_inv(domain_num, (unsigned long *)source_config-\u003eadm,\n+\t\t\t     AP_DOMAINS) {\n+\t\tif (!ap_test_config_ctrl_domain(domain_num)) {\n+\t\t\tpr_err(\"vfio_ap_mdev: %s: Control domain %04lx not available on the destination host\",\n+\t\t\t       mdev_name, domain_num);\n+\t\t\tavailable = false;\n+\t\t}\n+\t}\n+\n+\treturn available;\n+}\n+\n+static void report_facilities_compatibility(const char *mdev_name,\n+\t\t\t\t\t    unsigned long apqn,\n+\t\t\t\t\t    struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t\t    struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tif (src_hwinfo-\u003eapsc != target_hwinfo-\u003eapsc) {\n+\t\tif (src_hwinfo-\u003eapsc) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: APSC facility installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: APSC facility not installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: APSC facility not installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s APSC facility installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+\n+\tif (src_hwinfo-\u003emex4k != target_hwinfo-\u003emex4k) {\n+\t\tif (src_hwinfo-\u003emex4k) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: mex4k facility installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: mex4k facility not installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: mex4k facility not installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: mex4k facility installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+\n+\tif (src_hwinfo-\u003ecrt4k != target_hwinfo-\u003ecrt4k) {\n+\t\tif (src_hwinfo-\u003ecrt4k) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: crt4k facility installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: crt4k facility not installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: crt4k facility not installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: crt4k facility installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+}\n+\n+static void report_mode_compatibility(const char *mdev_name,\n+\t\t\t\t      unsigned long apqn,\n+\t\t\t\t      struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t      struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tif (src_hwinfo-\u003ecca != target_hwinfo-\u003ecca) {\n+\t\tif (src_hwinfo-\u003ecca) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Coprocessor-mode facility installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Coprocessor-mode  facility not installed target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Coprocessor-mode facility not installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Coprocessor-mode  facility installed target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+\n+\tif (src_hwinfo-\u003eaccel != target_hwinfo-\u003eaccel) {\n+\t\tif (src_hwinfo-\u003eaccel) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Accelerator-mode facility installed source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Accelerator-mode facility not installed target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Accelerator-mode facility not installed source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Accelerator-mode facility installed target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+\n+\tif (src_hwinfo-\u003eep11 != target_hwinfo-\u003eep11) {\n+\t\tif (src_hwinfo-\u003eep11) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: XCP-mode facility installed source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: XCP-mode facility not installed target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: XCP-mode facility not installed source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: XCP-mode facility installed target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+}\n+\n+static void report_apxa_compatibility(const char *mdev_name,\n+\t\t\t\t      unsigned long apqn,\n+\t\t\t\t      struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t      struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tif (src_hwinfo-\u003eapxa != target_hwinfo-\u003eapxa) {\n+\t\tif (src_hwinfo-\u003eapxa) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: AP-extended-addressing (APXA) facility installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: AP-extended-addressing (APXA) facility not installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: AP-extended-addressing (APXA) facility not installed in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: AP-extended-addressing (APXA) facility installed in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+}\n+\n+static void report_slcf_compatibility(const char *mdev_name,\n+\t\t\t\t      unsigned long apqn,\n+\t\t\t\t      struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t      struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tif (src_hwinfo-\u003eslcf != target_hwinfo-\u003eslcf) {\n+\t\tif (src_hwinfo-\u003eslcf) {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Stateless-command-filtering (SLCF) available in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Stateless-command-filtering (SLCF) not available in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t} else {\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Stateless-command-filtering (SLCF) not available in source queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\t\tpr_err(\"vfio_ap_mdev %s: Stateless-command-filtering (SLCF) available in target queue %02lx.%04lx\\n\",\n+\t\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t\t}\n+\t}\n+}\n+\n+static void report_bs_compatibility(const char *mdev_name,\n+\t\t\t\t    unsigned long apqn,\n+\t\t\t\t    struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t    struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\t/*\n+\t * The BS field on both the source and destination must be 0, so if one\n+\t * of them is not, then report an error.\n+\t */\n+\tif (src_hwinfo-\u003ebs || target_hwinfo-\u003ebs) {\n+\t\tpr_err(\"vfio_ap_mdev %s: Bind/associate state for source (%01x) and target (%01x) queue %02lx.%04lx must be 0\\n\",\n+\t\t       mdev_name, src_hwinfo-\u003ebs, target_hwinfo-\u003ebs,\n+\t\t       AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t}\n+}\n+\n+static void report_aptype_compatibility(const char *mdev_name,\n+\t\t\t\t\tunsigned long apqn,\n+\t\t\t\t\tstruct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t\tstruct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tif (src_hwinfo-\u003eat \u003e target_hwinfo-\u003eat) {\n+\t\tpr_err(\"vfio_ap_mdev %s: AP type of source (%02x) not compatible with target (%02x)\\n\",\n+\t\t       mdev_name, src_hwinfo-\u003eat, target_hwinfo-\u003eat);\n+\t}\n+}\n+\n+static bool classes_compatible(struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t       struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tunsigned long src_native, target_native;\n+\n+\tsrc_native = src_hwinfo-\u003eclass \u0026 CLASSIFICATION_NATIVE_FCN_MASK;\n+\ttarget_native = target_hwinfo-\u003eclass \u0026 CLASSIFICATION_NATIVE_FCN_MASK;\n+\n+\t/*\n+\t * If the source queue has full native card function and the\n+\t * target queue has only stateless functions available, then\n+\t * there may be instructions that will not execute on the\n+\t * target queue. This shall be reported as an error.\n+\t *\n+\t * If the source queue has only stateless card functions and the\n+\t * target queue has full native card function available, then\n+\t * we are okay because the target queue can run all stateless card\n+\t * functions.\n+\t */\n+\treturn (src_native != target_native) ? !src_native : true;\n+}\n+\n+static void report_class_compatibility(const char *mdev_name,\n+\t\t\t\t       unsigned long apqn,\n+\t\t\t\t       struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t       struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tif (!classes_compatible(src_hwinfo, target_hwinfo)) {\n+\t\tpr_err(\"vfio_ap_mdev %s: Full native card function available on source queue %02lx.%04lx\\n\",\n+\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\t\tpr_err(\"vfio_ap_mdev %s: Only stateless functions available on target queue %02lx.%04lx\\n\",\n+\t\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\t}\n+}\n+\n+/*\n+ * Log a device error reporting that migration failed due to queue\n+ * incompatibilities followed by a device error for each incompatible feature.\n+ */\n+static void report_qinfo_incompatibilities(const char *mdev_name,\n+\t\t\t\t\t   unsigned long apqn,\n+\t\t\t\t\t   struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t\t   struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tpr_err(\"vfio_ap_mdev %s: Migration failed: Source and target queue (%02lx.%04lx) not compatible\\n\",\n+\t       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));\n+\n+\treport_facilities_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\treport_mode_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\treport_apxa_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\treport_slcf_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\treport_aptype_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\treport_bs_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\treport_class_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+}\n+\n+/**\n+ * queue_hardware_info_is_compatible:\n+ *\n+ * Verify whether the hardware information for a source queue is compatible with\n+ * the hardware info for the corresponding queue on this system.\n+ *\n+ * In order to be compatible, the hardware information for each queue must\n+ * meet the following requirements:\n+ *\n+ * 1. The hardware facilities bits much match\n+ * 2. The AP type of the source queue must be the same as or older than that\n+ *    of the target queue (target is backwards compatible)\n+ * 3. The classification bits must indicate:\n+ *    - Both queues have full native card function or both have stateless\n+ *      functions available\n+ *    - If the classification bits don't match, then the only acceptable\n+ *      configuration is stateless functions for the source queue and\n+ *      full native function for the target queue\n+ * 4. The BS bits for both queues must be 0 (Queue usable for all messages\n+ *    supported by the adapter)\n+ *\n+ * @mdev_name:\tThe mdev name to use in error messages\n+ * @apqn:\tThe APQN for the queues\n+ * @src_hwinfo: The hardware info for the source queue\n+ * @target_hwinfo: The hardware info for the corresponding queue on this system\n+ *\n+ * Returns: true if the hardware info for the two queues is compatible;\n+ *          otherwise, returns false.\n+ */\n+static bool queue_hardware_info_is_compatible(const char *mdev_name,\n+\t\t\t\t\t      unsigned long apqn,\n+\t\t\t\t\t      struct ap_tapq_hwinfo *src_hwinfo,\n+\t\t\t\t\t      struct ap_tapq_hwinfo *target_hwinfo)\n+{\n+\tunsigned long src_bits, target_bits;\n+\n+\tsrc_bits = src_hwinfo-\u003evalue \u0026 QINFO_DATA_MASK;\n+\ttarget_bits = target_hwinfo-\u003evalue \u0026 QINFO_DATA_MASK;\n+\n+\t/* If all bits match the queues are compatible */\n+\tif (src_bits == target_bits \u0026\u0026\n+\t    (src_hwinfo-\u003ebs == 0 \u0026\u0026 target_hwinfo-\u003ebs == 0))\n+\t\treturn true;\n+\n+\tif (src_hwinfo-\u003eapsc  == target_hwinfo-\u003eapsc     \u0026\u0026\n+\t    src_hwinfo-\u003emex4k == target_hwinfo-\u003emex4k    \u0026\u0026\n+\t    src_hwinfo-\u003ecrt4k == target_hwinfo-\u003ecrt4k    \u0026\u0026\n+\t    src_hwinfo-\u003ecca   == target_hwinfo-\u003ecca      \u0026\u0026\n+\t    src_hwinfo-\u003eaccel == target_hwinfo-\u003eaccel    \u0026\u0026\n+\t    src_hwinfo-\u003eep11  == target_hwinfo-\u003eep11     \u0026\u0026\n+\t    src_hwinfo-\u003eslcf  == target_hwinfo-\u003eslcf     \u0026\u0026\n+\t    src_hwinfo-\u003eapxa  == target_hwinfo-\u003eapxa     \u0026\u0026\n+\t    src_hwinfo-\u003eat    \u003c= target_hwinfo-\u003eat       \u0026\u0026\n+\t    classes_compatible(src_hwinfo, target_hwinfo) \u0026\u0026\n+\t    (src_hwinfo-\u003ebs == 0 \u0026\u0026 target_hwinfo-\u003ebs == 0))\n+\t\treturn true;\n+\n+\treport_qinfo_incompatibilities(mdev_name, apqn, src_hwinfo, target_hwinfo);\n+\n+\treturn false;\n+}\n+\n+/**\n+ * verify_ap_configs_are_compatible:\n+ *\n+ * Verifies that the queues in the source guest's AP configuration are\n+ * compatible with the corresponding queues on this system.\n+ *\n+ * @mdev_name:\t   The mdev name to use in error messages\n+ * @source_config: The object specifying the source guest's AP configuration\n+ *\n+ * Returns: an error indicating either a failure to retrieve a queue's\n+ *\t\t\thardware information or one or more source queues are not\n+ *\t\t\tcompatible with the corresponding queue on this system; otherwise,\n+ *\t\t\treturns zero to indicate compatibility.\n+ */\n+static int verify_ap_configs_are_compatible(const char *mdev_name,\n+\t\t\t\t\t    struct vfio_ap_config *source_config)\n+{\n+\tstruct ap_tapq_hwinfo src_hwinfo, dest_hwinfo;\n+\tunsigned long apqn;\n+\tint ret = 0, rc;\n+\n+\tfor (int i = 0; i \u003c source_config-\u003enum_queues; i++) {\n+\t\tapqn = source_config-\u003eqinfo[i].apqn;\n+\n+\t\t/*\n+\t\t * If we can't get the hardware info for a particular queue, then let's\n+\t\t * capture the function return code and continue so we can log all\n+\t\t * errors to aid in debugging of migration.\n+\t\t */\n+\t\trc = get_hardware_info_for_queue(mdev_name, \u0026dest_hwinfo, apqn);\n+\t\tif (rc) {\n+\t\t\tret = rc;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tsrc_hwinfo.value =  source_config-\u003eqinfo[i].data;\n+\n+\t\tif (!queue_hardware_info_is_compatible(mdev_name, apqn,\n+\t\t\t\t\t\t       \u0026src_hwinfo,\n+\t\t\t\t\t\t       \u0026dest_hwinfo))\n+\t\t\tret = -EINVAL;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int do_post_copy_validation(const char *mdev_name,\n+\t\t\t\t   struct vfio_ap_config *source_config)\n+{\n+\tif (!queues_available(mdev_name, source_config))\n+\t\treturn -ENODEV;\n+\n+\tif (!control_domains_available(mdev_name, source_config))\n+\t\treturn -ENODEV;\n+\n+\treturn verify_ap_configs_are_compatible(mdev_name, source_config);\n+}\n+\n+/**\n+ * setup_ap_matrix_from_ap_config:\n+ *\n+ * Set the bits corresponding to the adapters, domains and control domains\n+ * from the source guest's AP configuration into an ap_matrix object to be\n+ * used to update the destination guest to run on this host.\n+ *\n+ * @ap_config:\t\tThe source guest's AP configuration\n+ * @guest_matrix:\tThe object to be used to update the destination guest's\n+ *\t\t\tAP configuration\n+ */\n+static void setup_ap_matrix_from_ap_config(struct vfio_ap_config *ap_config,\n+\t\t\t\t\t   struct ap_matrix *guest_matrix)\n+{\n+\tstruct ap_config_info host_config_info = { 0 };\n+\tunsigned long apid, apqi, *guest_adm;\n+\tstruct vfio_ap_queue_info qinfo;\n+\n+\tap_qci(\u0026host_config_info);\n+\tvfio_ap_matrix_init(\u0026host_config_info, guest_matrix);\n+\n+\tfor (int i = 0; i \u003c ap_config-\u003enum_queues; i++) {\n+\t\tqinfo = ap_config-\u003eqinfo[i];\n+\t\tapid = AP_QID_CARD(qinfo.apqn);\n+\t\tapqi = AP_QID_QUEUE(qinfo.apqn);\n+\n+\t\tif (!test_bit_inv(apid, guest_matrix-\u003eapm))\n+\t\t\tset_bit_inv(apid, guest_matrix-\u003eapm);\n+\t\tif (!test_bit_inv(apqi, guest_matrix-\u003eaqm))\n+\t\t\tset_bit_inv(apqi, guest_matrix-\u003eaqm);\n+\t}\n+\n+\tguest_adm = (unsigned long *)ap_config-\u003eadm;\n+\tfor_each_set_bit_inv(apqi, guest_adm, AP_DOMAINS) {\n+\t\tif (!test_bit_inv(apqi, guest_matrix-\u003eadm))\n+\t\t\tset_bit_inv(apqi, guest_matrix-\u003eadm);\n+\t}\n+}\n+\n+static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,\n+\t\t\t\t      size_t len, loff_t *pos)\n+{\n+\tstruct vfio_ap_migration_data *mig_data;\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tstruct vfio_ap_config *ap_config;\n+\tstruct ap_matrix guest_matrix;\n+\tssize_t ret = 0, cfg_sz;\n+\tconst char *mdev_name;\n+\n+\t/*\n+\t * When userspace calls write() with an explicit offset (pwrite), pos is\n+\t * non-NULL and the function rejects it with -ESPIPE (illegal seek). For\n+\t * normal write() calls, pos is NULL, so we'll use the file's internal\n+\t * position filp-\u003ef_pos\n+\t */\n+\tif (pos)\n+\t\treturn -ESPIPE;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tpos = \u0026filp-\u003ef_pos;\n+\n+\tret = validate_resuming_write_parms(filp, len, pos);\n+\tif (ret) {\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn ret;\n+\t}\n+\n+\tmatrix_mdev = filp-\u003eprivate_data;\n+\tmig_data = matrix_mdev-\u003emig_data;\n+\tmdev_name = dev_name(matrix_mdev-\u003evdev.dev);\n+\n+\t/*\n+\t * If this is the first write operation, then allocate storage for the\n+\t * AP configuration information; otherwise, reallocate the\n+\t * struct vfio_ap_config object used to store the AP configuration data\n+\t * sent from userspace.\n+\t */\n+\tif (*pos == 0) {\n+\t\tret = allocate_ap_config(\u0026ap_config, buf, len);\n+\n+\t\t/* If the allocation failed, we'll return the error */\n+\t\tif (ret \u003c 0) {\n+\t\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tcfg_sz = ret;\n+\t} else {\n+\t\tret = reallocate_ap_config(\u0026mig_data-\u003eresuming_mig_file,\n+\t\t\t\t\t   \u0026ap_config, len);\n+\t\tif (ret \u003c 0) {\n+\t\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tcfg_sz = ret;\n+\t}\n+\n+\tif (*pos + len \u003e cfg_sz) {\n+\t\tret = -EIO;\n+\t\tkfree(ap_config);\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn ret;\n+\t}\n+\n+\t/*\n+\t * We don't want to lock all mdevs while copying data from userspace so\n+\t * we don't block all other mdevs in case the I/O takes a long time.\n+\t * From here on out we don't need this lock because we are not\n+\t * accessing the matrix_mdev until we need to get the update\n+\t * locks to set the new destination guest's AP configuration in which\n+\t * case this lock will be taken then.\n+\t */\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tif (copy_from_user((char *)ap_config + *pos, buf, len)) {\n+\t\tret = -EIO;\n+\t\tkfree(ap_config);\n+\n+\t\treturn ret;\n+\t}\n+\n+\t/* Check if we've completed writing the entire configuration */\n+\tif (*pos + len == cfg_sz) {\n+\t\t/* Acquire locks required to update the guest's AP config */\n+\t\tmutex_lock(\u0026ap_attr_mutex);\n+\t\tget_update_locks_for_mdev(matrix_mdev);\n+\n+\t\t/*\n+\t\t * Verify the device wasn't closed while mdevs_lock was dropped\n+\t\t * for the copy_from_user above. get_update_locks_for_mdev()\n+\t\t * reacquires mdevs_lock, so the check is safe here.\n+\t\t */\n+\t\tif (!matrix_mdev-\u003emig_data) {\n+\t\t\trelease_update_locks_for_mdev(matrix_mdev);\n+\t\t\tmutex_unlock(\u0026ap_attr_mutex);\n+\t\t\tkfree(ap_config);\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\n+\t\tret = do_post_copy_validation(mdev_name, ap_config);\n+\t\tif (ret \u003c 0) {\n+\t\t\tkfree(ap_config);\n+\t\t\trelease_update_locks_for_mdev(matrix_mdev);\n+\t\t\tmutex_unlock(\u0026ap_attr_mutex);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tsetup_ap_matrix_from_ap_config(ap_config, \u0026guest_matrix);\n+\n+\t\tret = vfio_ap_set_new_guest_config(matrix_mdev, \u0026guest_matrix);\n+\n+\t\trelease_update_locks_for_mdev(matrix_mdev);\n+\t\tmutex_unlock(\u0026ap_attr_mutex);\n+\n+\t\tif (ret) {\n+\t\t\tkfree(ap_config);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tkfree(mig_data-\u003eresuming_mig_file.ap_config);\n+\tmig_data-\u003eresuming_mig_file.ap_config = ap_config;\n+\tmig_data-\u003eresuming_mig_file.config_sz = cfg_sz;\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tret = len;\n+\t*pos += len;\n+\n+\treturn ret;\n+}\n+\n+static const struct file_operations vfio_ap_resume_fops = {\n+\t.owner = THIS_MODULE,\n+\t.write = vfio_ap_resuming_write,\n+\t.release = vfio_ap_release_mig_file,\n+};\n+\n+static struct file *vfio_ap_resuming_init(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tstruct vfio_ap_migration_data *mig_data;\n+\tstruct file *filp;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tmig_data = matrix_mdev-\u003emig_data;\n+\tfilp = vfio_ap_open_file_stream(matrix_mdev, \u0026vfio_ap_resume_fops, O_WRONLY);\n+\n+\treturn filp;\n+}\n+\n+static struct file *\n+vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t    enum vfio_device_mig_state new_state)\n+{\n+\tstruct vfio_ap_migration_data *mig_data;\n+\tenum vfio_device_mig_state cur_state;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\tmig_data = matrix_mdev-\u003emig_data;\n+\tcur_state = mig_data-\u003emig_state;\n+\tdev_dbg(matrix_mdev-\u003evdev.dev, \"%s: %d -\u003e %d\\n\", __func__, cur_state,\n+\t\tnew_state);\n+\n+\t/*\n+\t * Begins the process of saving the vfio device state by creating and\n+\t * returning a streaming data_fd to be used to read out the internal\n+\t * state of the vfio-ap device on the source host.\n+\t */\n+\tif (cur_state == VFIO_DEVICE_STATE_STOP \u0026\u0026\n+\t    new_state == VFIO_DEVICE_STATE_STOP_COPY) {\n+\t\tstruct file *filp = vfio_ap_open_file_stream(matrix_mdev,\n+\t\t\t\t\t\t\t     \u0026vfio_ap_stop_copy_fops,\n+\t\t\t\t\t\t\t     O_RDONLY);\n+\t\tif (IS_ERR(filp))\n+\t\t\treturn ERR_CAST(filp);\n+\n+\t\tmig_data-\u003estop_copy_mig_file.filp = filp;\n+\n+\t\treturn filp;\n+\t}\n+\n+\t/*\n+\t * Begins the process of restoring the vfio device state by creating and\n+\t * returning a streaming data_fd to be used to read in the internal\n+\t * state of the vfio-ap device on the destination host.\n+\t */\n+\tif (cur_state == VFIO_DEVICE_STATE_STOP \u0026\u0026\n+\t    new_state == VFIO_DEVICE_STATE_RESUMING) {\n+\t\tstruct file *filp = vfio_ap_resuming_init(matrix_mdev);\n+\n+\t\tif (IS_ERR(filp))\n+\t\t\treturn ERR_CAST(filp);\n+\n+\t\tmig_data-\u003eresuming_mig_file.filp = filp;\n+\t\treturn filp;\n+\t}\n+\n+\t/*\n+\t * Terminates the data transfer session of the vfio-ap device state\n+\t * between the source and target hosts. Since the vfio-ap device does\n+\t * not virtualize a DMA device, there is no internal device state to\n+\t * incorporate into the vfio-ap device on the target.\n+\t */\n+\tif ((cur_state == VFIO_DEVICE_STATE_RESUMING \u0026\u0026\n+\t     new_state == VFIO_DEVICE_STATE_STOP) ||\n+\t    (cur_state == VFIO_DEVICE_STATE_STOP_COPY \u0026\u0026\n+\t     new_state == VFIO_DEVICE_STATE_STOP)) {\n+\t\treturn NULL;\n+\t}\n+\n+\t/*\n+\t * These states indicate migration has either not been initiated or\n+\t * has completed and the vfio-ap device is operating normally.Since the\n+\t * vfio-ap device does not virtualize a DMA device, there is no internal\n+\t * device state to incorporate into the vfio-ap device on the target.\n+\t */\n+\tif ((cur_state == VFIO_DEVICE_STATE_STOP \u0026\u0026\n+\t     new_state == VFIO_DEVICE_STATE_RUNNING) ||\n+\t    (cur_state == VFIO_DEVICE_STATE_RUNNING \u0026\u0026\n+\t     new_state == VFIO_DEVICE_STATE_STOP)) {\n+\t\treturn NULL;\n+\t}\n+\n+\t/* vfio_mig_get_next_state() does not use arcs other than the above */\n+\tWARN_ON(true);\n+\n+\treturn ERR_PTR(-EINVAL);\n+}\n+\n+static struct file *vfio_ap_set_state(struct vfio_device *vdev,\n+\t\t\t\t      enum vfio_device_mig_state  new_state)\n+{\n+\tint ret;\n+\tstruct file *filp = NULL;\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tenum vfio_device_mig_state next_state;\n+\tstruct vfio_ap_migration_data *mig_data;\n+\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tif (ap_is_se_guest()) {\n+\t\tdev_err_once(matrix_mdev-\u003evdev.dev,\n+\t\t\t     \"Migration not allowed from or to a Secure Execution guest\\n\");\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\treturn ERR_PTR(-EPERM);\n+\t}\n+\n+\tmig_data = matrix_mdev-\u003emig_data;\n+\n+\t/*\n+\t * The mig_data pointer is set in the vfio_ap_init_migration_data\n+\t * function which is called when the vfio-ap device fd is opened.\n+\t * Since the implicit pre-open state is RUNNING, a request to set\n+\t * RUNNING is a no-op. Any other state transition is invalid before\n+\t * open_device.\n+\t */\n+\tif (!mig_data) {\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\tif (new_state == VFIO_DEVICE_STATE_RUNNING)\n+\t\t\treturn NULL;\n+\t\treturn ERR_PTR(-ENODEV);\n+\t}\n+\n+\tdev_dbg(vdev-\u003edev, \"%s -\u003e %d\\n\", __func__, new_state);\n+\n+\twhile (mig_data-\u003emig_state != VFIO_DEVICE_STATE_ERROR \u0026\u0026\n+\t       mig_data-\u003emig_state != new_state) {\n+\t\tret = vfio_mig_get_next_state(vdev, mig_data-\u003emig_state,\n+\t\t\t\t\t      new_state, \u0026next_state);\n+\t\tif (ret) {\n+\t\t\tfilp = ERR_PTR(ret);\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tfilp = vfio_ap_transition_to_state(matrix_mdev, next_state);\n+\t\tif (IS_ERR(filp))\n+\t\t\tbreak;\n+\n+\t\tmig_data-\u003emig_state = next_state;\n+\n+\t\tif (WARN_ON(filp \u0026\u0026 new_state != next_state)) {\n+\t\t\tfput(filp);\n+\t\t\tfilp = ERR_PTR(-EINVAL);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\treturn filp;\n+}\n+\n+static int vfio_ap_get_state(struct vfio_device *vdev,\n+\t\t\t     enum vfio_device_mig_state  *current_state)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tstruct vfio_ap_migration_data *mig_data;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n+\tmig_data =  matrix_mdev-\u003emig_data;\n+\n+\t/*\n+\t * The mig_data pointer is set in the vfio_ap_init_migration_data\n+\t * function which is called when the vfio-ap device fd is opened.\n+\t * If mig_data is NULL, report RUNNING as the implicit pre-open state\n+\t * so userspace doesn't need to perform any state transition before the\n+\t * device becomes active.\n+\t */\n+\t*current_state = (mig_data) ? mig_data-\u003emig_state :\n+\t\t\t\t      VFIO_DEVICE_STATE_RUNNING;\n+\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\treturn 0;\n+}\n+\n+static int vfio_ap_get_data_size(struct vfio_device *vdev,\n+\t\t\t\t unsigned long *stop_copy_length)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tsize_t qinfo_sz;\n+\tint num_queues;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n+\tnum_queues = vfio_ap_mdev_get_num_queues(\u0026matrix_mdev-\u003eshadow_apcb);\n+\tqinfo_sz = num_queues * sizeof(struct vfio_ap_queue_info);\n+\t*stop_copy_length = qinfo_sz + sizeof(struct vfio_ap_config);\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\treturn 0;\n+}\n+\n+static const struct vfio_migration_ops vfio_ap_migration_ops = {\n+\t.migration_set_state = vfio_ap_set_state,\n+\t.migration_get_state = vfio_ap_get_state,\n+\t.migration_get_data_size = vfio_ap_get_data_size,\n+};\n+\n+/**\n+ * vfio_ap_init_migrations_capabilities - initialize migration capabilities\n+ *\n+ * @matrix_mdev: pointer to object containing the mdev state\n+ */\n+void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\t/* Live guest migration is not supported for SE guests */\n+\tif (ap_is_se_guest())\n+\t\treturn;\n+\n+\tmatrix_mdev-\u003evdev.migration_flags = VFIO_MIGRATION_STOP_COPY;\n+\tmatrix_mdev-\u003evdev.mig_ops = \u0026vfio_ap_migration_ops;\n+}\n+\n+/**\n+ * vfio_ap_init_migration_data - initialize migration data and functions\n+ *\n+ * @matrix_mdev: pointer to object containing the mdev state\n+ *\n+ * Return: zero if initialization is successful; otherwise, returns a error.\n+ */\n+int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tstruct vfio_ap_migration_data *mig_data;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tmig_data = kzalloc_obj(struct vfio_ap_migration_data, GFP_KERNEL);\n+\tif (!mig_data)\n+\t\treturn -ENOMEM;\n+\n+\tmig_data-\u003emig_state = VFIO_DEVICE_STATE_RUNNING;\n+\tmatrix_mdev-\u003emig_data = mig_data;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * vfio_ap_release_migration_data: reclaim private migration data\n+ *\n+ * @vdev: pointer to the mdev\n+ */\n+void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tif (!matrix_mdev-\u003emig_data)\n+\t\treturn;\n+\n+\tkfree(matrix_mdev-\u003emig_data-\u003eresuming_mig_file.ap_config);\n+\tkfree(matrix_mdev-\u003emig_data-\u003estop_copy_mig_file.ap_config);\n+\tkfree(matrix_mdev-\u003emig_data);\n+\tmatrix_mdev-\u003emig_data = NULL;\n+}\n+\n+static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tstruct vfio_ap_migration_data *mig_data;\n+\n+\t/*\n+\t * The fput call does not call .release synchronously while the\n+\t * mdevs_lock mutex is held, so there is no problem with incurring a\n+\t * deadlock situation if fput is executed in this function.\n+\t */\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tmig_data = matrix_mdev-\u003emig_data;\n+\tif (!mig_data)\n+\t\treturn;\n+\n+\tif (mig_data-\u003estop_copy_mig_file.filp) {\n+\t\tfput(mig_data-\u003estop_copy_mig_file.filp);\n+\t\tmig_data-\u003estop_copy_mig_file.filp = NULL;\n+\t}\n+\n+\tkfree(mig_data-\u003estop_copy_mig_file.ap_config);\n+\tmig_data-\u003estop_copy_mig_file.ap_config = NULL;\n+\tmig_data-\u003estop_copy_mig_file.config_sz = 0;\n+\n+\tif (mig_data-\u003eresuming_mig_file.filp) {\n+\t\tfput(mig_data-\u003eresuming_mig_file.filp);\n+\t\tmig_data-\u003eresuming_mig_file.filp = NULL;\n+\t}\n+\n+\tkfree(mig_data-\u003eresuming_mig_file.ap_config);\n+\tmig_data-\u003eresuming_mig_file.ap_config = NULL;\n+\tmig_data-\u003eresuming_mig_file.config_sz = 0;\n+}\n+\n+/**\n+ * vfio_ap_reset_migration_state - Reset the vfio-ap migration state\n+ *\n+ * @matrix_mdev: pointer to the object maintaining the vfio-ap device state\n+ *\n+ * Called during VFIO_DEVICE_RESET to clean up any active migration\n+ * state and reset the device to RUNNING state as required by the VFIO\n+ * migration specification.\n+ */\n+void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tif (!matrix_mdev-\u003emig_data)\n+\t\treturn;\n+\n+\tvfio_ap_release_mig_files(matrix_mdev);\n+\tmatrix_mdev-\u003emig_data-\u003emig_state = VFIO_DEVICE_STATE_RUNNING;\n+}\ndiff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c\nindex 44b3a1dcc1b365..454bb9fe197168 100644\n--- a/drivers/s390/crypto/vfio_ap_ops.c\n+++ b/drivers/s390/crypto/vfio_ap_ops.c\n@@ -81,53 +81,6 @@ static inline void release_update_locks_for_kvm(struct kvm *kvm)\n \tmutex_unlock(\u0026matrix_dev-\u003eguests_lock);\n }\n \n-/**\n- * get_update_locks_for_mdev: Acquire the locks required to dynamically update a\n- *\t\t\t      KVM guest's APCB in the proper order.\n- *\n- * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP\n- *\t\t configuration data to use to update a KVM guest's APCB.\n- *\n- * The proper locking order is:\n- * 1. matrix_dev-\u003eguests_lock: required to use the KVM pointer to update a KVM\n- *\t\t\t       guest's APCB.\n- * 2. matrix_mdev-\u003ekvm-\u003elock:  required to update a guest's APCB\n- * 3. matrix_dev-\u003emdevs_lock:  required to access data stored in a matrix_mdev\n- *\n- * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM\n- *\t lock will not be taken.\n- */\n-static inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)\n-{\n-\tmutex_lock(\u0026matrix_dev-\u003eguests_lock);\n-\tif (matrix_mdev \u0026\u0026 matrix_mdev-\u003ekvm)\n-\t\tmutex_lock(\u0026matrix_mdev-\u003ekvm-\u003elock);\n-\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n-}\n-\n-/**\n- * release_update_locks_for_mdev: Release the locks used to dynamically update a\n- *\t\t\t\t  KVM guest's APCB in the proper order.\n- *\n- * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP\n- *\t\t configuration data to use to update a KVM guest's APCB.\n- *\n- * The proper unlocking order is:\n- * 1. matrix_dev-\u003emdevs_lock\n- * 2. matrix_mdev-\u003ekvm-\u003elock\n- * 3. matrix_dev-\u003eguests_lock\n- *\n- * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM\n- *\t lock will not be released.\n- */\n-static inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)\n-{\n-\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n-\tif (matrix_mdev \u0026\u0026 matrix_mdev-\u003ekvm)\n-\t\tmutex_unlock(\u0026matrix_mdev-\u003ekvm-\u003elock);\n-\tmutex_unlock(\u0026matrix_dev-\u003eguests_lock);\n-}\n-\n /**\n  * get_update_locks_by_apqn: Find the mdev to which an APQN is assigned and\n  *\t\t\t     acquire the locks required to update the APCB of\n@@ -642,8 +595,7 @@ static int handle_pqap(struct kvm_vcpu *vcpu)\n \treturn 0;\n }\n \n-static void vfio_ap_matrix_init(struct ap_config_info *info,\n-\t\t\t\tstruct ap_matrix *matrix)\n+void vfio_ap_matrix_init(struct ap_config_info *info, struct ap_matrix *matrix)\n {\n \tmatrix-\u003eapm_max = info-\u003eapxa ? info-\u003ena : 63;\n \tmatrix-\u003eaqm_max = info-\u003eapxa ? info-\u003end : 15;\n@@ -775,18 +727,30 @@ static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev,\n \n static int vfio_ap_mdev_init_dev(struct vfio_device *vdev)\n {\n-\tstruct ap_matrix_mdev *matrix_mdev =\n-\t\tcontainer_of(vdev, struct ap_matrix_mdev, vdev);\n+\tstruct ap_matrix_mdev *matrix_mdev;\n \n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n \tmatrix_mdev-\u003emdev = to_mdev_device(vdev-\u003edev);\n \tvfio_ap_matrix_init(\u0026matrix_dev-\u003einfo, \u0026matrix_mdev-\u003ematrix);\n \tmatrix_mdev-\u003epqap_hook = handle_pqap;\n \tvfio_ap_matrix_init(\u0026matrix_dev-\u003einfo, \u0026matrix_mdev-\u003eshadow_apcb);\n \thash_init(matrix_mdev-\u003eqtable.queues);\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n \n \treturn 0;\n }\n \n+static void vfio_ap_mdev_release_dev(struct vfio_device *vdev)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n+\tvfio_ap_release_migration_data(matrix_mdev);\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+}\n+\n static int vfio_ap_mdev_probe(struct mdev_device *mdev)\n {\n \tstruct ap_matrix_mdev *matrix_mdev;\n@@ -797,18 +761,29 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)\n \tif (IS_ERR(matrix_mdev))\n \t\treturn PTR_ERR(matrix_mdev);\n \n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\t/*\n+\t * Migration capabilities must be initialized before calling\n+\t * vfio_register_emulated_iommu_dev; otherwise, the VFIO core\n+\t * will see mig_ops as NULL during the registration. This could\n+\t * prevent the VFIO core from properly setting up migration\n+\t * infrastructure like debugfs entries.\n+\t */\n+\tvfio_ap_init_migration_capabilities(matrix_mdev);\n+\n \tret = vfio_register_emulated_iommu_dev(\u0026matrix_mdev-\u003evdev);\n \tif (ret)\n \t\tgoto err_put_vdev;\n \tmatrix_mdev-\u003ereq_trigger = NULL;\n \tmatrix_mdev-\u003ecfg_chg_trigger = NULL;\n \tdev_set_drvdata(\u0026mdev-\u003edev, matrix_mdev);\n-\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n \tlist_add(\u0026matrix_mdev-\u003enode, \u0026matrix_dev-\u003emdev_list);\n \tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n \treturn 0;\n \n err_put_vdev:\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n \tvfio_put_device(\u0026matrix_mdev-\u003evdev);\n \treturn ret;\n }\n@@ -852,7 +827,7 @@ static void vfio_ap_mdev_unlink_fr_queues(struct ap_matrix_mdev *matrix_mdev)\n \t\t\tq = vfio_ap_mdev_get_queue(matrix_mdev,\n \t\t\t\t\t\t   AP_MKQID(apid, apqi));\n \t\t\tif (q)\n-\t\t\t\tq-\u003ematrix_mdev = NULL;\n+\t\t\t\tvfio_ap_mdev_link_queue(matrix_mdev, q);\n \t\t}\n \t}\n }\n@@ -991,19 +966,20 @@ static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev,\n \tunsigned long apqi;\n \n \tfor_each_set_bit_inv(apqi, matrix_mdev-\u003ematrix.aqm, AP_DOMAINS)\n-\t\tvfio_ap_mdev_link_apqn(matrix_mdev,\n-\t\t\t\t       AP_MKQID(apid, apqi));\n+\t\tvfio_ap_mdev_link_apqn(matrix_mdev, AP_MKQID(apid, apqi));\n }\n \n-static void collect_queues_to_reset(struct ap_matrix_mdev *matrix_mdev,\n-\t\t\t\t    unsigned long apid,\n-\t\t\t\t    struct list_head *qlist)\n+static void collect_queues_by_apid(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t\t   unsigned long apid,\n+\t\t\t\t   struct list_head *qlist)\n {\n \tstruct vfio_ap_queue *q;\n \tunsigned long  apqi;\n \n \tfor_each_set_bit_inv(apqi, matrix_mdev-\u003eshadow_apcb.aqm, AP_DOMAINS) {\n-\t\tq = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi));\n+\t\tq = matrix_mdev ?\n+\t\t\t\tvfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)) :\n+\t\t\t\tvfio_ap_find_queue(AP_MKQID(apid, apqi));\n \t\tif (q)\n \t\t\tlist_add_tail(\u0026q-\u003ereset_qnode, qlist);\n \t}\n@@ -1015,7 +991,7 @@ static void reset_queues_for_apid(struct ap_matrix_mdev *matrix_mdev,\n \tstruct list_head qlist;\n \n \tINIT_LIST_HEAD(\u0026qlist);\n-\tcollect_queues_to_reset(matrix_mdev, apid, \u0026qlist);\n+\tcollect_queues_by_apid(matrix_mdev, apid, \u0026qlist);\n \tvfio_ap_mdev_reset_qlist(\u0026qlist);\n }\n \n@@ -1031,7 +1007,7 @@ static int reset_queues_for_apids(struct ap_matrix_mdev *matrix_mdev,\n \tINIT_LIST_HEAD(\u0026qlist);\n \n \tfor_each_set_bit_inv(apid, apm_reset, AP_DEVICES)\n-\t\tcollect_queues_to_reset(matrix_mdev, apid, \u0026qlist);\n+\t\tcollect_queues_by_apid(matrix_mdev, apid, \u0026qlist);\n \n \treturn vfio_ap_mdev_reset_qlist(\u0026qlist);\n }\n@@ -1702,54 +1678,100 @@ static void ap_matrix_copy(struct ap_matrix *dst, struct ap_matrix *src)\n \tbitmap_copy(dst-\u003eadm, src-\u003eadm, AP_DOMAINS);\n }\n \n-static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr,\n-\t\t\t       const char *buf, size_t count)\n+static void get_removed_matrixes(struct ap_matrix *m_removed,\n+\t\t\t\t struct ap_matrix *m_old,\n+\t\t\t\t struct ap_matrix *m_new)\n {\n-\tstruct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);\n-\tstruct ap_matrix m_new, m_old, m_added, m_removed;\n+\tbitmap_andnot(m_removed-\u003eapm, m_old-\u003eapm, m_new-\u003eapm, AP_DEVICES);\n+\tbitmap_andnot(m_removed-\u003eaqm, m_old-\u003eaqm, m_new-\u003eaqm, AP_DOMAINS);\n+\tbitmap_andnot(m_removed-\u003eadm, m_old-\u003eadm, m_new-\u003eadm, AP_DOMAINS);\n+}\n+\n+static void get_added_matrixes(struct ap_matrix *m_added,\n+\t\t\t       struct ap_matrix *m_old,\n+\t\t\t       struct ap_matrix *m_new)\n+{\n+\tbitmap_andnot(m_added-\u003eapm, m_new-\u003eapm, m_old-\u003eapm, AP_DEVICES);\n+\tbitmap_andnot(m_added-\u003eaqm, m_new-\u003eaqm, m_old-\u003eaqm, AP_DOMAINS);\n+\tbitmap_andnot(m_added-\u003eadm, m_new-\u003eadm, m_old-\u003eadm, AP_DOMAINS);\n+}\n+\n+static int validate_new_state(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tint rc;\n+\n+\t/* Ensure new state is valid, else undo new state */\n+\trc = vfio_ap_mdev_validate_masks(matrix_mdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\trc = ap_matrix_overflow_check(matrix_mdev);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\treturn 0;\n+}\n+\n+static void link_new_queues(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t    struct ap_matrix *m_added)\n+{\n+\tunsigned long apid, apqi;\n+\n+\tfor_each_set_bit_inv(apid, m_added-\u003eapm, AP_DEVICES)\n+\t\tvfio_ap_mdev_link_adapter(matrix_mdev, apid);\n+\n+\tfor_each_set_bit_inv(apqi, m_added-\u003eaqm, AP_DOMAINS)\n+\t\tvfio_ap_mdev_link_domain(matrix_mdev, apqi);\n+}\n+\n+/**\n+ * vfio_ap_set_new_guest_config:\n+ *\n+ * Set a new AP configuration for a guest.\n+ *\n+ * @matrix_mdev: Object used to maintain the AP configuration for a guest\n+ * @m_new:\t\t Object used to set the new AP configuration\n+ *\n+ * Returns: zero (0) if the new AP configuration is successfully set; otherwise,\n+ *\t    returns an error:\n+ *\n+ *\t    ~ EADDRNOTAVAIL One or more APQNs are reserved for host use\n+ *\t    ~ EADDRINUSE    One or more APQNs are assigned to another mdev\n+ *\t    ~ ENODEV\t    An adapter, domain or control domain in the new\n+ *\t\t\t    AP configuration exceeds the max architected value\n+ */\n+int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t\t struct ap_matrix *m_new)\n+{\n+\tstruct ap_matrix m_old, m_old_shadow, m_added, m_removed;\n \tDECLARE_BITMAP(apm_filtered, AP_DEVICES);\n-\tunsigned long newbit;\n-\tchar *newbuf, *rest;\n-\tint rc = count;\n \tbool do_update;\n+\tint rc;\n \n-\tnewbuf = kstrndup(buf, AP_CONFIG_STRLEN, GFP_KERNEL);\n-\tif (!newbuf)\n-\t\treturn -ENOMEM;\n-\trest = newbuf;\n-\n-\tmutex_lock(\u0026ap_attr_mutex);\n-\tget_update_locks_for_mdev(matrix_mdev);\n+\tlockdep_assert_held(\u0026ap_attr_mutex);\n+\tassert_has_update_locks_for_mdev(matrix_mdev);\n \n-\t/* Save old state */\n+\t/* Save the old state */\n \tap_matrix_copy(\u0026m_old, \u0026matrix_mdev-\u003ematrix);\n-\tif (parse_bitmap(\u0026rest, m_new.apm, AP_DEVICES) ||\n-\t    parse_bitmap(\u0026rest, m_new.aqm, AP_DOMAINS) ||\n-\t    parse_bitmap(\u0026rest, m_new.adm, AP_DOMAINS)) {\n-\t\trc = -EINVAL;\n-\t\tgoto out;\n-\t}\n+\tap_matrix_copy(\u0026m_old_shadow, \u0026matrix_mdev-\u003eshadow_apcb);\n \n-\tbitmap_andnot(m_removed.apm, m_old.apm, m_new.apm, AP_DEVICES);\n-\tbitmap_andnot(m_removed.aqm, m_old.aqm, m_new.aqm, AP_DOMAINS);\n-\tbitmap_andnot(m_added.apm, m_new.apm, m_old.apm, AP_DEVICES);\n-\tbitmap_andnot(m_added.aqm, m_new.aqm, m_old.aqm, AP_DOMAINS);\n+\t/*\n+\t * Get the adapters, domains and control domains added and/or removed\n+\t * from the existing configuration\n+\t */\n+\tget_removed_matrixes(\u0026m_removed, \u0026m_old, m_new);\n+\tget_added_matrixes(\u0026m_added, \u0026m_old, m_new);\n \n \t/* Need new bitmaps in matrix_mdev for validation */\n-\tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, \u0026m_new);\n+\tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, m_new);\n \n \t/* Ensure new state is valid, else undo new state */\n-\trc = vfio_ap_mdev_validate_masks(matrix_mdev);\n-\tif (rc) {\n-\t\tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, \u0026m_old);\n-\t\tgoto out;\n-\t}\n-\trc = ap_matrix_overflow_check(matrix_mdev);\n+\trc = validate_new_state(matrix_mdev);\n \tif (rc) {\n \t\tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, \u0026m_old);\n-\t\tgoto out;\n+\t\tap_matrix_copy(\u0026matrix_mdev-\u003eshadow_apcb, \u0026m_old_shadow);\n+\t\treturn rc;\n \t}\n-\trc = count;\n \n \t/* Need old bitmaps in matrix_mdev for unplug/unlink */\n \tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, \u0026m_old);\n@@ -1759,14 +1781,10 @@ static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr\n \tvfio_ap_mdev_hot_unplug_domains(matrix_mdev, m_removed.aqm);\n \n \t/* Need new bitmaps in matrix_mdev for linking new adapters/domains */\n-\tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, \u0026m_new);\n-\n-\t/* Link newly added adapters */\n-\tfor_each_set_bit_inv(newbit, m_added.apm, AP_DEVICES)\n-\t\tvfio_ap_mdev_link_adapter(matrix_mdev, newbit);\n+\tap_matrix_copy(\u0026matrix_mdev-\u003ematrix, m_new);\n \n-\tfor_each_set_bit_inv(newbit, m_added.aqm, AP_DOMAINS)\n-\t\tvfio_ap_mdev_link_domain(matrix_mdev, newbit);\n+\t/* Link queues associated with the newly added adapters and domains */\n+\tlink_new_queues(matrix_mdev, \u0026m_added);\n \n \t/* filter resources not bound to vfio-ap */\n \tdo_update = vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered);\n@@ -1777,7 +1795,39 @@ static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr\n \t\tvfio_ap_mdev_update_guest_apcb(matrix_mdev);\n \t\treset_queues_for_apids(matrix_mdev, apm_filtered);\n \t}\n-out:\n+\n+\treturn 0;\n+}\n+\n+static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr,\n+\t\t\t       const char *buf, size_t count)\n+{\n+\tstruct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);\n+\tstruct ap_matrix m_new;\n+\tchar *newbuf, *rest;\n+\tssize_t rc;\n+\n+\tnewbuf = kstrndup(buf, AP_CONFIG_STRLEN, GFP_KERNEL);\n+\tif (!newbuf)\n+\t\treturn -ENOMEM;\n+\trest = newbuf;\n+\n+\tmutex_lock(\u0026ap_attr_mutex);\n+\tget_update_locks_for_mdev(matrix_mdev);\n+\n+\tif (parse_bitmap(\u0026rest, m_new.apm, AP_DEVICES) ||\n+\t    parse_bitmap(\u0026rest, m_new.aqm, AP_DOMAINS) ||\n+\t    parse_bitmap(\u0026rest, m_new.adm, AP_DOMAINS)) {\n+\t\tkfree(newbuf);\n+\t\trelease_update_locks_for_mdev(matrix_mdev);\n+\t\tmutex_unlock(\u0026ap_attr_mutex);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\trc = vfio_ap_set_new_guest_config(matrix_mdev, \u0026m_new);\n+\tif (!rc)\n+\t\trc = count;\n+\n \trelease_update_locks_for_mdev(matrix_mdev);\n \tmutex_unlock(\u0026ap_attr_mutex);\n \tkfree(newbuf);\n@@ -2052,19 +2102,39 @@ static int vfio_ap_mdev_reset_qlist(struct list_head *qlist)\n \n static int vfio_ap_mdev_open_device(struct vfio_device *vdev)\n {\n-\tstruct ap_matrix_mdev *matrix_mdev =\n-\t\tcontainer_of(vdev, struct ap_matrix_mdev, vdev);\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\tint ret;\n \n \tif (!vdev-\u003ekvm)\n \t\treturn -EINVAL;\n \n-\treturn vfio_ap_mdev_set_kvm(matrix_mdev, vdev-\u003ekvm);\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n+\tret = vfio_ap_init_migration_data(matrix_mdev);\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = vfio_ap_mdev_set_kvm(matrix_mdev, vdev-\u003ekvm);\n+\tif (ret) {\n+\t\t/* Clean up migration data on failure */\n+\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\t\tvfio_ap_release_migration_data(matrix_mdev);\n+\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\t}\n+\n+\treturn ret;\n }\n \n static void vfio_ap_mdev_close_device(struct vfio_device *vdev)\n {\n-\tstruct ap_matrix_mdev *matrix_mdev =\n-\t\tcontainer_of(vdev, struct ap_matrix_mdev, vdev);\n+\tstruct ap_matrix_mdev *matrix_mdev;\n+\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n+\tvfio_ap_release_migration_data(matrix_mdev);\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n \n \tvfio_ap_mdev_unset_kvm(matrix_mdev);\n }\n@@ -2098,6 +2168,20 @@ static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)\n \trelease_update_locks_for_mdev(matrix_mdev);\n }\n \n+int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix)\n+{\n+\tunsigned long apid, apqi;\n+\tint num_queues = 0;\n+\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+\n+\tfor_each_set_bit_inv(apid, ap_matrix-\u003eapm, AP_DEVICES)\n+\t\tfor_each_set_bit_inv(apqi, ap_matrix-\u003eaqm, AP_DOMAINS)\n+\t\t\tnum_queues++;\n+\n+\treturn num_queues;\n+}\n+\n static int vfio_ap_mdev_get_device_info(unsigned long arg)\n {\n \tunsigned long minsz;\n@@ -2275,6 +2359,10 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,\n \t\tbreak;\n \tcase VFIO_DEVICE_RESET:\n \t\tret = vfio_ap_mdev_reset_queues(matrix_mdev);\n+\t\tif (ret == 0) {\n+\t\t\t/* Reset migration state per VFIO migration spec */\n+\t\t\tvfio_ap_reset_migration_state(matrix_mdev);\n+\t\t}\n \t\tbreak;\n \tcase VFIO_DEVICE_GET_IRQ_INFO:\n \t\tret = vfio_ap_get_irq_info(arg);\n@@ -2360,6 +2448,7 @@ static const struct attribute_group vfio_queue_attr_group = {\n \n static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {\n \t.init = vfio_ap_mdev_init_dev,\n+\t.release = vfio_ap_mdev_release_dev,\n \t.open_device = vfio_ap_mdev_open_device,\n \t.close_device = vfio_ap_mdev_close_device,\n \t.ioctl = vfio_ap_mdev_ioctl,\ndiff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h\nindex 9bff666b0b35d3..150dfce8a674b2 100644\n--- a/drivers/s390/crypto/vfio_ap_private.h\n+++ b/drivers/s390/crypto/vfio_ap_private.h\n@@ -91,6 +91,9 @@ struct ap_queue_table {\n \tDECLARE_HASHTABLE(queues, 8);\n };\n \n+/* Forward declaration for migration data structure */\n+struct vfio_ap_migration_data;\n+\n /**\n  * struct ap_matrix_mdev - Contains the data associated with a matrix mediated\n  *\t\t\t   device.\n@@ -110,6 +113,7 @@ struct ap_queue_table {\n  * @aqm_add:\tbitmap of APQIs added to the host's AP configuration\n  * @adm_add:\tbitmap of control domain numbers added to the host's AP\n  *\t\tconfiguration\n+ * @mig_data:  vfio device migration data\n  */\n struct ap_matrix_mdev {\n \tstruct vfio_device vdev;\n@@ -125,6 +129,7 @@ struct ap_matrix_mdev {\n \tDECLARE_BITMAP(apm_add, AP_DEVICES);\n \tDECLARE_BITMAP(aqm_add, AP_DOMAINS);\n \tDECLARE_BITMAP(adm_add, AP_DOMAINS);\n+\tstruct vfio_ap_migration_data *mig_data;\n };\n \n /**\n@@ -152,6 +157,64 @@ struct vfio_ap_queue {\n \tstruct work_struct reset_work;\n };\n \n+/**\n+ * get_update_locks_for_mdev: Acquire the locks required to dynamically update a\n+ *\t\t\t      KVM guest's APCB in the proper order.\n+ *\n+ * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP\n+ *\t\t configuration data to use to update a KVM guest's APCB.\n+ *\n+ * The proper locking order is:\n+ * 1. matrix_dev-\u003eguests_lock: required to use the KVM pointer to update a KVM\n+ *\t\t\t       guest's APCB.\n+ * 2. matrix_mdev-\u003ekvm-\u003elock:  required to update a guest's APCB\n+ * 3. matrix_dev-\u003emdevs_lock:  required to access data stored in a matrix_mdev\n+ *\n+ * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM\n+ *\t lock will not be taken.\n+ */\n+static inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tmutex_lock(\u0026matrix_dev-\u003eguests_lock);\n+\tif (matrix_mdev \u0026\u0026 matrix_mdev-\u003ekvm)\n+\t\tmutex_lock(\u0026matrix_mdev-\u003ekvm-\u003elock);\n+\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n+}\n+\n+/**\n+ * release_update_locks_for_mdev: Release the locks used to dynamically update a\n+ *\t\t\t\t  KVM guest's APCB in the proper order.\n+ *\n+ * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP\n+ *\t\t configuration data to use to update a KVM guest's APCB.\n+ *\n+ * The proper unlocking order is:\n+ * 1. matrix_dev-\u003emdevs_lock\n+ * 2. matrix_mdev-\u003ekvm-\u003elock\n+ * 3. matrix_dev-\u003eguests_lock\n+ *\n+ * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM\n+ *\t lock will not be released.\n+ */\n+static inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n+\tif (matrix_mdev \u0026\u0026 matrix_mdev-\u003ekvm)\n+\t\tmutex_unlock(\u0026matrix_mdev-\u003ekvm-\u003elock);\n+\tmutex_unlock(\u0026matrix_dev-\u003eguests_lock);\n+}\n+\n+static inline void\n+assert_has_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)\n+{\n+\tlockdep_assert_held(\u0026matrix_dev-\u003eguests_lock);\n+\tif (matrix_mdev \u0026\u0026 matrix_mdev-\u003ekvm)\n+\t\tlockdep_assert_held(\u0026matrix_mdev-\u003ekvm-\u003elock);\n+\tlockdep_assert_held(\u0026matrix_dev-\u003emdevs_lock);\n+}\n+\n+int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix);\n+\n int vfio_ap_mdev_register(void);\n void vfio_ap_mdev_unregister(void);\n \n@@ -165,4 +228,14 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,\n void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,\n \t\t\t      struct ap_config_info *old_config_info);\n \n+void vfio_ap_matrix_init(struct ap_config_info *info, struct ap_matrix *matrix);\n+\n+void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev);\n+int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev);\n+void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev);\n+void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev);\n+\n+int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,\n+\t\t\t\t struct ap_matrix *m_new);\n+\n #endif /* _VFIO_AP_PRIVATE_H_ */\n"
}

2/1 2026/07/26 08:49 agent patch-evaluator
2m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch implements live guest migration for the s390 vfio_ap (Adjunct Processor) mediated device driver. The vfio_ap driver requires physical IBM Z AP cryptographic hardware to be present on the host system. During module initialization, `vfio_ap_init()` checks `ap_instructions_available()`, which executes a hardware `PQAP` instruction. If the hardware does not support it, the driver returns `-ENODEV` and fails to load. Standard QEMU/KVM environments do not emulate AP cryptographic hardware in software. Therefore, the code paths introduced in this patch are impossible to reach in standard virtualized environments without specific physical hardware, making it unsuitable for fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit c925b396b5604e9b7562291129db04b592192ec8
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Jul 26 08:49:10 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/arch/s390/vfio-ap.rst b/Documentation/arch/s390/vfio-ap.rst
index ac0c07f76ddd7b..02d7cbe5e47918 100644
--- a/Documentation/arch/s390/vfio-ap.rst
+++ b/Documentation/arch/s390/vfio-ap.rst
@@ -1012,7 +1012,7 @@ guest_matrix dyn ap_config
 
 the following features are advertised:
 
----------------+---------------------------------------------------------------+
++--------------+---------------------------------------------------------------+
 | Flag         | Description                                                   |
 +==============+===============================================================+
 | guest_matrix | guest_matrix attribute exists. It reports the matrix of       |
@@ -1021,105 +1021,523 @@ the following features are advertised:
 +--------------+---------------------------------------------------------------+
 | dyn          | Indicates hot plug/unplug of AP adapters, domains and control |
 |              | domains for a guest to which the mdev is attached.            |
-+------------+-----------------------------------------------------------------+
++--------------+---------------------------------------------------------------+
 | ap_config    | ap_config interface for one-shot modifications to mdev config |
 +--------------+---------------------------------------------------------------+
+| migratable   | Indicates that live guest migration is supported for guests   |
+|              | to which crypto devices are passed through                    |
++--------------+---------------------------------------------------------------+
 
-Limitations
-===========
-Live guest migration is not supported for guests using AP devices without
-intervention by a system administrator. Before a KVM guest can be migrated,
-the vfio_ap mediated device must be removed. Unfortunately, it can not be
-removed manually (i.e., echo 1 > /sys/devices/vfio_ap/matrix/$UUID/remove) while
-the mdev is in use by a KVM guest. If the guest is being emulated by QEMU,
-its mdev can be hot unplugged from the guest in one of two ways:
-
-1. If the KVM guest was started with libvirt, you can hot unplug the mdev via
-   the following commands:
-
-      virsh detach-device <guestname> <path-to-device-xml>
-
-      For example, to hot unplug mdev 62177883-f1bb-47f0-914d-32a22e3a8804 from
-      the guest named 'my-guest':
-
-         virsh detach-device my-guest ~/config/my-guest-hostdev.xml
-
-            The contents of my-guest-hostdev.xml:
-
-.. code-block:: xml
-
-            <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-ap'>
-              <source>
-                <address uuid='62177883-f1bb-47f0-914d-32a22e3a8804'/>
-              </source>
-            </hostdev>
-
-
-      virsh qemu-monitor-command <guest-name> --hmp "device-del <device-id>"
-
-      For example, to hot unplug the vfio_ap mediated device identified on the
-      qemu command line with 'id=hostdev0' from the guest named 'my-guest':
-
-.. code-block:: sh
-
-         virsh qemu-monitor-command my-guest --hmp "device_del hostdev0"
-
-2. A vfio_ap mediated device can be hot unplugged by attaching the qemu monitor
-   to the guest and using the following qemu monitor command:
-
-      (QEMU) device-del id=<device-id>
-
-      For example, to hot unplug the vfio_ap mediated device that was specified
-      on the qemu command line with 'id=hostdev0' when the guest was started:
-
-         (QEMU) device-del id=hostdev0
-
-After live migration of the KVM guest completes, an AP configuration can be
-restored to the KVM guest by hot plugging a vfio_ap mediated device on the target
-system into the guest in one of two ways:
-
-1. If the KVM guest was started with libvirt, you can hot plug a matrix mediated
-   device into the guest via the following virsh commands:
-
-   virsh attach-device <guestname> <path-to-device-xml>
-
-      For example, to hot plug mdev 62177883-f1bb-47f0-914d-32a22e3a8804 into
-      the guest named 'my-guest':
-
-         virsh attach-device my-guest ~/config/my-guest-hostdev.xml
-
-            The contents of my-guest-hostdev.xml:
-
-.. code-block:: xml
-
-            <hostdev mode='subsystem' type='mdev' managed='no' model='vfio-ap'>
-              <source>
-                <address uuid='62177883-f1bb-47f0-914d-32a22e3a8804'/>
-              </source>
-            </hostdev>
-
-
-   virsh qemu-monitor-command <guest-name> --hmp \
-   "device_add vfio-ap,sysfsdev=<path-to-mdev>,id=<device-id>"
-
-      For example, to hot plug the vfio_ap mediated device
-      62177883-f1bb-47f0-914d-32a22e3a8804 into the guest named 'my-guest' with
-      device-id hostdev0:
-
-      virsh qemu-monitor-command my-guest --hmp \
-      "device_add vfio-ap,\
-      sysfsdev=/sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804,\
-      id=hostdev0"
-
-2. A vfio_ap mediated device can be hot plugged by attaching the qemu monitor
-   to the guest and using the following qemu monitor command:
-
-      (qemu) device_add "vfio-ap,sysfsdev=<path-to-mdev>,id=<device-id>"
-
-      For example, to plug the vfio_ap mediated device
-      62177883-f1bb-47f0-914d-32a22e3a8804 into the guest with the device-id
-      hostdev0:
+Live Guest Migration
+====================
+The VFIO AP mediated device is not used to provide userspace with direct
+access to device memory as is the case with other devices that use the VFIO
+framework for device pass-through. Instead, it manages AP
+configuration metadata identifying the adapters, domains, and control
+domains to which a guest will be granted access. These AP resources are
+configured by assigning them to a vfio-ap mediated device via its sysfs
+assignment interfaces. When the guest is started, the vfio_ap device driver
+sets the guest's AP configuration from the metadata stored with the mediated
+device. The AP devices are not accessed directly through the vfio_ap driver,
+so the driver does not migrate the state of the AP devices themselves.
+Instead, it migrates the AP configuration metadata. The vfio_ap device
+driver on the destination host will then ensure that the AP configuration
+metadata migrated from the source host is compatible with the AP
+configuration available on the destination host and if
+so, will set the destination guest's AP configuration accordingly.
 
-         (QEMU) device-add "vfio-ap,\
-         sysfsdev=/sys/devices/vfio_ap/matrix/62177883-f1bb-47f0-914d-32a22e3a8804,\
-         id=hostdev0"
+To be considered compatible, the AP configuration for the destination host must
+meet these requirements:
+
+* Each AP adapter, domain and control domain number assigned to the source
+  guest's AP configuration must be in the destination host's AP
+  configuration. This can be verified as follows:
+
+  * On the source host, display the AP configuration of the source guest
+    with the following two commands:
+
+    .. code-block:: bash
+
+       cat /sys/devices/vfio_ap/matrix/$UUID/guest_matrix
+       cat /sys/devices/vfio_ap/matrix/$UUID/control_domains
+
+    .. note::
+       Replace ``$UUID`` with the actual UUID of your mediated device.
+
+       The ``guest_matrix`` lists the APQNs of the queue devices assigned
+       to the mediated device; for example:
+
+       .. code-block:: bash
+
+          00.0033
+          00.003a
+
+       The ``control_domains`` lists the domain numbers of the control
+       domains; for example:
+
+       .. code-block:: bash
+
+          0033
+          003a
+
+  * Display the AP configuration of the destination host with the following
+    two commands:
+
+    .. code-block:: bash
+
+       lszcrypt -V
+       cat /sys/bus/ap/ap_control_domain_mask
+
+    .. note::
+       The ``lszcrypt -V`` command lists the AP cards (adapters) and queue
+       devices on the host system; for example:
+
+       .. code-block:: bash
+
+          =========== ===== ============ ==========
+          CARD.DOMAIN TYPE  MODE         DRIVER
+          =========== ===== ============ ==========
+          00          CEX5C CCA-Coproc   cex4card
+          00.0033     CEX5C CCA-Coproc   vfio_ap
+          00.003a     CEX5C CCA-Coproc   vfio_ap
+          =========== ===== ============ ==========
+
+       Each bit in the ap_control_domain_mask corresponds to
+       domain numbers 0-255. If a bit is set to one, then
+       the control domain is available on the source host. For
+       example:
+
+       .. code-block:: bash
+
+          0x0000000000001020000000000000000000000000000000000000000000000000
+
+       This mask indicates that AP control domains 51 (0x33) and 58 (0x3a) are
+       in the AP configuration of the host system.
+
+  * Verify the configurations:
+
+    * Each APQN in the guest's sysfs ``guest_matrix`` file must match an
+      APQN returned from the ``lszcrypt -V`` command on the destination host. It
+      is important to note the driver to which the queue is bound which
+      is displayed in the ``driver`` column. Queues not bound to the vfio_ap
+      driver are not available to the destination guest. Even if the APQNs are
+      available to the driver, that does not guarantee they can be used by the
+      guest being migrated; for example, migration will fail if any of these APQNs
+      are assigned to a mediated device other than the one being migrated.
+
+    * Each domain number from the source guest's sysfs
+      ``control_domains`` attribute must correspond to a bit set to one in
+      the destination host's ``ap_control_domain_mask``.
+
+
+* Each APQN assigned to the source guest and destination guest must
+  reference a queue device with compatible hardware capabilities:
+
+  +--------------+---------------------------------------------------------------+
+  | Hardware     | Description                                                   |
+  | Capabilities |                                                               |
+  +==============+===============================================================+
+  | [1]          | * AP special command facility (APSC)                          |
+  | Facilities   | * AP 4096-bit ME PKU commands facility (AP4KM)                |
+  |              | * AP 4096-bit CRT PKU commands (AP4KC)                        |
+  +--------------+---------------------------------------------------------------+
+  | [1] Mode     | * CCA-mode                                                    |
+  |              | * Accelerator-mode                                            |
+  |              | * XCP-mode (EP11)                                             |
+  +--------------+---------------------------------------------------------------+
+  | [1] AP       |   APXA installed                                              |
+  | extended     |                                                               |
+  | addressing   |                                                               |
+  +--------------+---------------------------------------------------------------+
+  | [1] Command  |   Command filtering available                                 |
+  | filtering    |                                                               |
+  +--------------+---------------------------------------------------------------+
+  | [2]          | * Full native card function                                   |
+  | Functional   | * Only stateless functions                                    |
+  | capabilities |                                                               |
+  +--------------+---------------------------------------------------------------+
+  | [3] AP type  | * 10 (0x0a): CEX4                                             |
+  |              | * 11 (0x0b): CEX5                                             |
+  |              | * 12 (0x0c)  CEX6                                             |
+  |              | * 13 (0x0d)  CEX7                                             |
+  |              | * 14 (0x0e)  CEX8                                             |
+  +--------------+---------------------------------------------------------------+
+
+  +-----------------------------------------------------------------------------+
+  | Table Keys                                                                  |
+  +=============================================================================+
+  |                                                                             |
+  | [1]        The hardware capability must be the same for both the source     |
+  |            and destination queue device.                                    |
+  |                                                                             |
+  | [2]        The source and destination queues must either both have          |
+  |            full native card function or both have stateless functions.      |
+  |            If the functional capabilities don't match, then the source      |
+  |            queue can have stateless capabilities since full native card     |
+  |            function can handle stateless functions; otherwise, migration    |
+  |            will fail.                                                       |
+  |                                                                             |
+  | [3]        The AP type on the source and destination guests can             |
+  |            differ if the queue passed through to the target guest is a      |
+  |            newer model (backwards compatible)                               |
+  +-----------------------------------------------------------------------------+
+
+  * To verify the hardware capabilities are compatible:
+
+    * On both the source and destination hosts, display the hardware capabilities
+      for each AP adapter in the AP configuration for that host:
+
+      .. code-block:: bash
+
+         cat /sys/bus/ap/devices/card$APID/ap_functions
+
+      .. note::
+         The ``$APID`` is the two-character adapter number in hexadecimal format;
+         for example, ``card02`` or ``card1f``. The ``/sys/bus/ap/devices`` directory
+         also lists the APQNs of the queue devices installed in the respective
+         host system with the first two characters being the APID; for example:
+         ``02.0004`` or ``00.0033`` .
+
+    * Verify the hardware capabilities for each AP adapter device on the source
+      and destination host are compatible. The ``ap_functions`` attribute is a
+      bitmask. The bits in the mask read from left to right starting with bit 0.
+      Each bit that is set to one indicates the corresponding hardware capability
+      is installed:
+
+      * Bits 0-2 are the facilities bits. Each bit value must match for the AP
+        devices on the source and destination systems. The values indicate the
+        following:
+
+        * bit-0: APSC is installed
+        * bit-1: AP4KM is installed
+        * bit-2: AP4KC is installed
+
+      * Bits 3-5 specify the adapter Modes. Only one of these bits will be set to
+        one and must be the same for both the source and destination adapter
+        devices:
+
+        * bit-3: CCA-mode
+        * bit-4: Accelerator-mode
+        * bit-5: XCP-mode (EP11)
+
+      * Bit 6 specifies whether APXA is installed and must be the same for both
+        the source and destination adapter devices.
+
+      * Bit 7 specifies whether command filtering is installed
+        and must be the same for both the source and destination adapter devices.
+
+      * Bits 8-15 are the classification bits. There are only two relevant
+        bits and only one of them will be set to one. For the source and destination
+        adapter devices to be considered compatible:
+
+        * Bit 8 may be set to 1 on both hosts (full native card function)
+        * Bit 9 may be set to 1 on both hosts (only stateless functions available)
+        * Bit 9 may be set to 1 on the source host and bit 8 may be set to 1 on
+          the destination host
+
+      * Bits 16-17 are the binding state bits which must be 00
+
+  * To verify the AP types are compatible:
+
+    * On both the source and destination hosts, display the hardware type
+      for each AP adapter in the AP configuration for that host to ensure the
+      source and destination adapter types are the same, or the source type is
+      less than the destination type::
+
+       cat /sys/bus/ap/devices/card$APID/hwtype
+
+      .. note::
+         The ``$APID`` is the two-character adapter number in hexadecimal format;
+         for example, ``card02`` or ``card1f``.
+
+Setting up for live guest migration on the destination host
+-----------------------------------------------------------
+To set up a host as the destination for live guest migration, do the
+following:
+
+1. Create a mediated device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Create a mediated device with the same name (UUID) as the mediated device
+used to supply the AP configuration to the source guest. For example, if
+the mediated device on the source guest is named
+62177883-f1bb-47f0-914d-32a22e3a8804:
+
+.. code-block:: bash
+
+   echo 62177883-f1bb-47f0-914d-32a22e3a8804 > \
+     /sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/create
+
+2. Reserve adapters and domains for the vfio_ap device driver's use
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Reserve the same adapters, and domains passed through to the source guest
+for the vfio_ap device driver on the destination host by clearing the
+adapter numbers from the ``/sys/bus/ap/apmask`` and/or the domain numbers
+from the ``/sys/bus/ap/aqmask`` files.
+
+For example, suppose adapters 0, 20 (0x14) and 22 (0x16) as well as
+domains 51 (0x33) and 58 (0x3a) are passed through to the source guest. To
+ensure they can be passed through to the destination guest, they must be
+made available to the vfio_ap device driver on the destination host. To
+make them available, do one or both of the following on the destination host:
+
+.. code-block:: bash
+
+   echo -0,-0x14,-22 > /sys/bus/ap/apmask
+   echo -0,-0x33,-0x3a > /sys/bus/ap/aqmask
+
+.. note::
+
+    * It is not necessary to reserve both the adapters and domains.
+      Reserving an adapter implicitly allows the vfio_ap driver to assign
+      each of the domains available on the system along with the reserved
+      adapters to a guest's AP configuration and vice versa.
+
+    * The Cartesian product of the adapters and domains reserved for the
+      vfio_ap device driver identifies the APQNs of the AP queue devices to be
+      bound to the vfio_ap device driver. If an APQN does not reference a
+      queue device installed in the system at the time of live guest migration,
+      the migration will fail.
+
+    * It is not necessary to configure the destination mediated device with
+      adapters, domains and control domains because the source guest's AP
+      configuration will overlay the destination guest's during migration.
+
+    * To verify that the queue devices are reserved for the vfio_ap
+      device driver, issue the ``lszcrypt -V`` command and you should
+      see ``vfio_ap`` in the ``DRIVER`` column of the output.
+
+Live guest migration failures due to AP configuration errors
+------------------------------------------------------------
+This section describes the errors that may occur during live guest migration
+when the AP configuration of the source guest and destination host are not
+compatible and where to look for problem determination.
+
+The destination host is missing the mediated device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This error occurs when a mediated device with the same name (UUID) as the
+mediated device being migrated has not been created on the destination host.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: device not found: mediated device '$UUID' not           |
+|              | found                                                          |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | initiating migration                                           |
+|              | qemu-system-s390x: Sibling indicated error 1                   |
++--------------+----------------------------------------------------------------+
+
+.. note::
+   $UUID is the UUID of the mediated device being migrated from the source host.
+
+
+One or more queues are not bound to the vfio_ap driver on the destination host
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The queue devices passed through to the source guest are not installed in the
+destination host or are not bound to its vfio_ap device driver.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: No such device: Failed to load vmstate version_id: 1,  |
+|              | ret: -19                                                       |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Kernel log:  | vfio_ap_mdev $UUID: Queue $APID.$APQI not available to         |
+|              | vfio_ap driver on target host                                  |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | shutting down, reason=failed                                   |
+|              | qemu-system-s390x: terminating on signal $SIGNAL_NUM from      |
+|              | pid $PID $DAEMON                                               |
++--------------+----------------------------------------------------------------+
+
+
+One or more control domains are not available on the destination host
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The control domains passed through to the source guest are not installed on the
+destination host.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: No such device: Failed to load vmstate version_id: 1,  |
+|              | ret: -19                                                       |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Kernel log:  | vfio_ap_mdev $UUID: Control domain $DOMAIN_NUM not available   |
+|              | on the destination host                                        |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | shutting down, reason=failed                                   |
+|              | qemu-system-s390x: terminating on signal $SIGNAL_NUM from      |
+|              | pid $PID $DAEMON                                               |
++--------------+----------------------------------------------------------------+
+
+
+.. note::
+   * $UUID is the UUID of the mediated device being migrated from the source host.
+   * $APID.$APQI is adapter number and queue index (domain number) comprising the
+     $APQN (for example, 00.0033) of the queue device that is not available.
+   * $SIGNAL_NUM is the signal number that caused the QEMU process to terminate.
+   * $PID is the process ID of the QEMU process on the destination host.
+   * $DAEMON is the name of the QEMU daemon process.
+   * This message will be logged for each queue for which this error is detected.
+
+Failure to get hardware information for a queue device on destination host
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The vfio_ap device driver on the destination host will execute an AP instruction
+to get the hardware information for a queue device to perform queue compatibility
+checks. The instruction can fail for a number of reasons in which case the
+migration will be terminated.
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: failed to get hardware information for queue device    |
+|              | $APQN                                                          |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | vfio_ap_mdev $UUID: migration failed: Failed to get hwinfo for |
+|              | queue $APQN on target host: rc=$RESPONSE-CODE                  |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | shutting down, reason=failed                                   |
+|              | qemu-system-s390x: terminating on signal $SIGNAL-ID from pid   |
+|              | $PID $DAEMON                                                   |
++--------------+----------------------------------------------------------------+
+
+.. note::
+   * $UUID is the UUID of the mediated device being migrated from the source host.
+   * $APQN (for example, 00.0033) of the queue device that is not compatible.
+   * $RESPONSE_CODE is the response code from the AP instruction.
+   * $SIGNAL_NUM is the signal number of the signal that caused the QEMU process
+     to terminate.
+   * $PID is the process ID of the QEMU process on the destination host.
+   * $DAEMON is the name of the QEMU daemon process.
+   * This message will be logged for each queue for which this error is detected.
+
+The AP configurations of the source guest and destination host not compatible
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The functional capabilities or facilities available on one or more of the queues
+passed through to the source guest are not compatible with the queue device with
+the same APQN on the destination system (see the Hardware Capabilities table above)
+
+**Source host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | error: operation failed: migration failed. Message from the    |
+|              | source host: operation failed: job 'migration out' failed:     |
+|              | Sibling indicated error 1. Message from the destination host:  |
+|              | operation failed: job 'migration in' failed: load of migration |
+|              | failed: No such device: Failed to load vmstate version_id: 1,  |
+|              | ret: -19                                                       |
++--------------+----------------------------------------------------------------+
+| Kernel log:  | N/A                                                            |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | *initiating migration*                                         |
+|              |                                                                |
+|              | *qemu-system-s390x: Sibling indicated error 1*                 |
++--------------+----------------------------------------------------------------+
+
+**Destination host**
+
++--------------+----------------------------------------------------------------+
+| Log          | Message                                                        |
++==============+================================================================+
+| Console log: | N/A                                                            |
++--------------+----------------------------------------------------------------+
+| Kernel log:  | *vfio_ap_mdev $UUID: Migration failed: Source and target       |
+|              | queue ($APQN) not compatible*                                  |
+|              |                                                                |
+|              | The message above will be followed by one or more messages     |
+|              | enumerating the incompatible features; for example:            |
+|              |                                                                |
+|              | ``vfio_ap_mdev $UUID: APSC facility installed in source queue  |
+|              | $APQN``                                                        |
+|              | ``vfio_ap_mdev $UUID: APSC facility not installed in target    |
+|              | queue $APQN``                                                  |
+|              |                                                                |
+|              | ``AP type of source ($APTYPE1) not compatible with target      |
+|              | ($APTYPE2)`` ...                                               |
++--------------+----------------------------------------------------------------+
+| QEMU log:    | *initiating migration*                                         |
+|              |                                                                |
+|              | *qemu-system-s390x: error while loading state section id ...*  |
+|              |                                                                |
+|              | *shutting down, reason=failed*                                 |
+|              |                                                                |
+|              | *terminating on signal $SIGNAL-ID from pid $PID $DAEMON*       |
++--------------+----------------------------------------------------------------+
+
+.. note::
+   * $UUID is the UUID of the mediated device being migrated from the source host.
+   * $APQN (for example, 00.0033) of the queue device that is incompatible.
+   * $APTYPE1 is the type of the source queue device.
+   * $APTYPE2 is the type of the target queue device.
+   * $SIGNAL-ID is the signal number of the signal that caused the QEMU process
+     to terminate.
+   * $PROCESS-ID is the process ID of the QEMU process on the source host.
+   * $PID is the process ID of the QEMU process on the destination host.
+   * $DAEMON is the name of the QEMU daemon process.
+   * Each follow-on message logging the incompatibility will be logged for each
+     error detected for the specified queue device.
+
+AP Configuration Management
+---------------------------
+The AP configurations of the source guest and destination host must be kept
+in synchronization or live guest migration will likely fail due to potential
+incompatibilities introduced by such changes. In particular, changing adapter
+or domain reservations via the sysfs ``apmask`` or ``aqmask`` attributes on
+either the source or destination host without coordinating such changes between
+the two hosts -especially while a migration is taking place - can result in
+migration failures.
+
+Configuration stability is an orchestration-layer or system administrator
+responsibility, which is consistent with other VFIO device types. The vfio_ap
+device driver will validate source and destination configurations and provide
+clear diagnostics when incompatibilities are detected, enabling orchestration
+tools to implement appropriate policies; for example, blocking migration when
+such changes are to be made.
+
+Master Key administration
+--------------------------
+It is imperative that s390 Common Cryptographic Architecture (CCA) master key
+administration be performed on both the source and destination AP
+devices to synchronize the key values prior to allowing live guest
+migration. If the master keys do not match, then crypto applications that
+rely on secure keys wrapped by a CCA master key will fail when the guest on
+which they are running is migrated to the destination host. Again, this is
+an orchestration-layer or system administrator responsibility and migration must
+be blocked while master key administration is being performed.
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index e83c6603c8587d..20f29184825a7b 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_drv.c b/drivers/s390/crypto/vfio_ap_drv.c
index 8e69ed286bb9ca..4e973736522bc1 100644
--- a/drivers/s390/crypto/vfio_ap_drv.c
+++ b/drivers/s390/crypto/vfio_ap_drv.c
@@ -26,9 +26,11 @@ MODULE_LICENSE("GPL v2");
 struct ap_matrix_dev *matrix_dev;
 debug_info_t *vfio_ap_dbf_info;
 
+#define FEATURES "guest_matrix hotplug ap_config migratable"
+
 static ssize_t features_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	return sysfs_emit(buf, "guest_matrix hotplug ap_config\n");
+	return sysfs_emit(buf, "%s\n", FEATURES);
 }
 static DEVICE_ATTR_RO(features);
 
diff --git a/drivers/s390/crypto/vfio_ap_migration.c b/drivers/s390/crypto/vfio_ap_migration.c
new file mode 100644
index 00000000000000..20b519f0898f5d
--- /dev/null
+++ b/drivers/s390/crypto/vfio_ap_migration.c
@@ -0,0 +1,1560 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Drives vfio_ap mdev migration.
+ *
+ * Copyright IBM Corp. 2025
+ */
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include "vfio_ap_private.h"
+
+/*
+ * Masks the fields of the queue information returned from the PQAP(TAPQ)
+ * command. In order to migrate a guest, it's AP configuration must be
+ * compatible with AP configuration assigned to the target guest's mdev.
+ * This mask is used to verify that the queue information for each source and
+ * target queue is compatible.
+ *
+ * The following bits must match for the source device and the corresponding
+ * destination device:
+ * -------------------------------------------------------------------------
+ * S bit 0: APSC  facility installed
+ * M bit 1: APQKM facility installed
+ * C bit 2: AP4KC facility installed
+ * Mode bits 3-5:
+ *     D bit 3: CCA-mode facility
+ *     A bit 4: accelerator-mode facility
+ *     X bit 5: XCP-mode facility
+ * N  bit 6: APXA facility installed
+ * SL bit 7: SLCF facility installed
+ *
+ * Either bit 8 or bit 9 will be set. If bit 8 is set for the source device,
+ * then it must also be set for the corresponding destination device:
+ * -------------------------------------------------------------------------
+ * Classification (functional capabilities) bits 8-16
+ *     bit 8: Native card function
+ *     bit 9: Only stateless functions
+ *
+ * The BS bits must be set to 0 for both the source and corresponding
+ * destination device:
+ * -------------------------------------------------------------------------
+ * BS bits 16-17:
+ *
+ * The AP type of the source device must be less than or equal to that of
+ * the corresponding destination device:
+ * -------------------------------------------------------------------------
+ * AP Type bits 32-40:
+ */
+#define QINFO_DATA_MASK		0xffffc000ff000000
+
+/*
+ * Masks the bit that indicates whether full native card function is available
+ * from the 8 bits specifying the functional capabilities of a queue
+ */
+#define CLASSIFICATION_NATIVE_FCN_MASK		0x80
+
+/* The maximum number of queues that can be installed in an s390 system */
+#define MAX_AP_QUEUES				(AP_DEVICES * AP_DOMAINS)
+
+/**
+ * vfio_ap_migration_file
+ *
+ * This object is used for chunk processing of multiple reads and writes of
+ * AP configuration information.
+ *
+ * @filp:	file stream used to read or write AP configuration data
+ * @ap_config:	object used to store AP configuration data between read or write
+ *		calls
+ * @config_sz:	the size (in bytes) of @ap_config
+ */
+struct vfio_ap_migration_file {
+	struct file		*filp;
+	struct vfio_ap_config	*ap_config;
+	unsigned long		config_sz;
+};
+
+/**
+ * vfio_ap_migration_data
+ *
+ * Manages the migration state for the VFIO device that maintains the AP
+ * configuration of the guest being migrated.
+ *
+ * @mig_state:		the current migration state
+ * @resuming_mig_file:	the object used to restore the state of the vfio-ap
+ *			device the destination host:
+ * @stop_copy_mig_file: the object used to store the AP configuration of the
+ *			source guest for transfer to the destination host.
+ */
+struct vfio_ap_migration_data {
+	enum vfio_device_mig_state	mig_state;
+	struct vfio_ap_migration_file	resuming_mig_file;
+	struct vfio_ap_migration_file	stop_copy_mig_file;
+};
+
+/**
+ * vfio_ap_queue_info - the information for an AP queue
+ *
+ * @data: contains the queue information returned in GR2 from the PQAP(TAPQ)
+ *	  command
+ * @apqn: the APQN of the queue
+ * @reserved: padding to ensure consistent structure size across platforms
+ */
+struct vfio_ap_queue_info {
+	u64 data;
+	u16 apqn;
+	u8  reserved[6];
+};
+
+/**
+ * vfio_ap_config - the guest's AP configuration
+ *
+ * @num_queues:	the number of queues passed through to the guest
+ * @reserved:	padding to ensure proper alignment of @cntl_domains
+ * @adm:	bitmap specifying the control domains in the AP configuration
+ * @qinfo:	an array of vfio_ap_queue_info objects, each specifying the
+ *		queue information for a queue passed through to the guest
+ */
+struct vfio_ap_config {
+	u32				num_queues;
+	u8				reserved[4];
+	u64				adm[DIV_ROUND_UP(AP_DOMAINS, 64)];
+	struct vfio_ap_queue_info	qinfo[] __counted_by(num_queues);
+};
+
+static void
+vfio_ap_release_stop_copy_file(struct vfio_ap_migration_data *mig_data)
+{
+	kfree(mig_data->stop_copy_mig_file.ap_config);
+	mig_data->stop_copy_mig_file.ap_config = NULL;
+	mig_data->stop_copy_mig_file.config_sz = 0;
+	mig_data->stop_copy_mig_file.filp = NULL;
+}
+
+static void
+vfio_ap_release_resuming_file(struct vfio_ap_migration_data *mig_data)
+{
+	kfree(mig_data->resuming_mig_file.ap_config);
+	mig_data->resuming_mig_file.ap_config = NULL;
+	mig_data->resuming_mig_file.config_sz = 0;
+	mig_data->resuming_mig_file.filp = NULL;
+}
+
+static int vfio_ap_release_mig_file(struct inode *file_inode, struct file *filp)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	int ret = 0;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	matrix_mdev = filp->private_data;
+	if (!matrix_mdev || !matrix_mdev->mig_data) {
+		ret = -ENODEV;
+		goto done;
+	}
+
+	if (filp == matrix_mdev->mig_data->stop_copy_mig_file.filp)
+		vfio_ap_release_stop_copy_file(matrix_mdev->mig_data);
+	else if (filp == matrix_mdev->mig_data->resuming_mig_file.filp)
+		vfio_ap_release_resuming_file(matrix_mdev->mig_data);
+	else
+		ret = -ENOENT;
+
+done:
+	mutex_unlock(&matrix_dev->mdevs_lock);
+	return ret;
+}
+
+/**
+ * validate_stop_copy_read_parms: Validate the input parameters to the
+ *                                vfio_ap_stop_copy_read function
+ *
+ * @matrix_mdev: The object device containing the state to be read
+ * @filp: Pointer to the file stream used to read the vfio-ap device state
+ * @pos:  The file offset from which to start reading data
+ * @len:  The length of the data to be read
+ *
+ * Verify the following:
+ * - @filp private data is an ap_matrix_mdev instance
+ * - @filp is the instance opened when state transitioned from STOP to STOP_COPY
+ * - @pos + @len does not cause integer overflow
+ *
+ * Returns: 0 if the parameters pass validation; otherwise returns an error
+ */
+static int validate_stop_copy_read_parms(struct file *filp, loff_t *pos,
+					 size_t len)
+{
+	struct vfio_ap_migration_data *mig_data;
+	struct ap_matrix_mdev *matrix_mdev;
+	loff_t total_len;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (check_add_overflow((loff_t)len, *pos, &total_len))
+		return -EIO;
+
+	matrix_mdev = filp->private_data;
+
+	if (!matrix_mdev || !matrix_mdev->mig_data)
+		return -ENODEV;
+
+	mig_data = matrix_mdev->mig_data;
+
+	if (mig_data->stop_copy_mig_file.filp != filp)
+		return -EINVAL;
+
+	return 0;
+}
+
+static size_t vfio_ap_config_size(struct ap_matrix_mdev *matrix_mdev,
+				  int *num_queues)
+{
+	size_t qinfo_size;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	*num_queues = vfio_ap_mdev_get_num_queues(&matrix_mdev->shadow_apcb);
+	qinfo_size = *num_queues * sizeof(struct vfio_ap_queue_info);
+
+	return qinfo_size + sizeof(struct vfio_ap_config);
+}
+
+static int get_hardware_info_for_queue(const char *mdev_name,
+				       struct ap_tapq_hwinfo *hwinfo,
+				       unsigned long apqn)
+{
+	struct ap_queue_status status;
+
+	status = ap_tapq(apqn, hwinfo);
+
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+	case AP_RESPONSE_RESET_IN_PROGRESS:
+	case AP_RESPONSE_DECONFIGURED:
+	case AP_RESPONSE_CHECKSTOPPED:
+	case AP_RESPONSE_BUSY:
+		/* For all these RCs the tapq info should be available */
+		return 0;
+	case AP_RESPONSE_Q_NOT_AVAIL:
+		pr_err("vfio_ap_mdev %s: Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+		       status.response_code);
+		return -ENODEV;
+	default:
+		/*
+		 * Without a pending async error, the tapq info should be
+		 * available
+		 */
+		if (status.async)
+			return 0;
+
+		pr_err("vfio_ap_mdev %s:Failed to get hwinfo for queue %02lx.%04lx: TAPQ rc=%d",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
+		       status.response_code);
+		return -EIO;
+	}
+}
+
+static int vfio_ap_store_queue_info(const char *mdev_name,
+				    struct vfio_ap_config *ap_config)
+{
+	struct ap_tapq_hwinfo source_hwinfo;
+	unsigned long num_queues;
+	int ret;
+
+	/*
+	 * ap_tapq() is a hardware instruction that may take time to complete.
+	 * It must be called without mdevs_lock held to avoid blocking other
+	 * mdevs. The apqn list was already snapshotted into ap_config->qinfo[]
+	 * by the caller under the lock.
+	 */
+	for (num_queues = 0; num_queues < ap_config->num_queues; num_queues++) {
+		ret = get_hardware_info_for_queue(mdev_name, &source_hwinfo,
+						  ap_config->qinfo[num_queues].apqn);
+		if (ret)
+			return ret;
+
+		ap_config->qinfo[num_queues].data = source_hwinfo.value;
+	}
+
+	return 0;
+}
+
+static int
+vfio_ap_get_config(struct ap_matrix_mdev *matrix_mdev)
+{
+	unsigned long *apm, *aqm, apid, apqi, num_queues;
+	struct vfio_ap_config *ap_configuration;
+	const char *mdev_name;
+	size_t ap_config_size;
+	int ret;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	ap_config_size = vfio_ap_config_size(matrix_mdev, (int *)&num_queues);
+
+	ap_configuration = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);
+	if (!ap_configuration)
+		return -ENOMEM;
+
+	/*
+	 * Snapshot the APQN list from shadow_apcb under the lock so that
+	 * ap_tapq() calls in vfio_ap_store_queue_info() can happen without it.
+	 */
+	apm = matrix_mdev->shadow_apcb.apm;
+	aqm = matrix_mdev->shadow_apcb.aqm;
+	num_queues = 0;
+	for_each_set_bit_inv(apid, apm, AP_DEVICES) {
+		for_each_set_bit_inv(apqi, aqm, AP_DOMAINS) {
+			ap_configuration->qinfo[num_queues].apqn =
+				AP_MKQID(apid, apqi);
+			num_queues += 1;
+		}
+	}
+	ap_configuration->num_queues = num_queues;
+	memcpy(ap_configuration->adm, matrix_mdev->shadow_apcb.adm,
+	       sizeof(ap_configuration->adm));
+	mdev_name = dev_name(matrix_mdev->vdev.dev);
+
+	/**
+	 * Release the global mdevs_lock which guards access to all mdevs.
+	 * Storing the queue information could take a while if there are a
+	 * large number of queues.
+	 */
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	ret = vfio_ap_store_queue_info(mdev_name, ap_configuration);
+	if (ret) {
+		kfree(ap_configuration);
+		mutex_lock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	/* Retake the mdevs lock so we can safely make updates to the mdev */
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	/*
+	 * Verify mig_data is still valid - mdevs_lock was dropped, so the
+	 * device could have been closed concurrently.
+	 */
+	if (!matrix_mdev->mig_data) {
+		kfree(ap_configuration);
+		return -ENODEV;
+	}
+
+	matrix_mdev->mig_data->stop_copy_mig_file.ap_config = ap_configuration;
+	matrix_mdev->mig_data->stop_copy_mig_file.config_sz = ap_config_size;
+
+	return 0;
+}
+
+static ssize_t vfio_ap_stop_copy_read(struct file *filp, char __user *buf,
+				      size_t len, loff_t *pos)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	struct vfio_ap_config *ap_config;
+	ssize_t ret = 0;
+	size_t ap_config_size;
+
+	/*
+	 * When userspace calls read() with an explicit offset (pread), pos is
+	 * non-NULL and the function rejects it with -ESPIPE (illegal seek). For
+	 * normal read() calls, pos is NULL, so we'll use the file's internal
+	 * position filp->f_pos
+	 */
+	if (pos)
+		return -ESPIPE;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	pos = &filp->f_pos;
+
+	ret = validate_stop_copy_read_parms(filp, pos, len);
+	if (ret) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	matrix_mdev = filp->private_data;
+	if (!matrix_mdev->mig_data->stop_copy_mig_file.ap_config) {
+		ret = vfio_ap_get_config(matrix_mdev);
+		if (ret) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			return ret;
+		}
+	}
+
+	ap_config_size = matrix_mdev->mig_data->stop_copy_mig_file.config_sz;
+
+	/*
+	 * If the position exceeds the size of the AP configuration data,
+	 * then indicate EOF; otherwise calculate the length of the data to
+	 * read such that a buffer overrun is prevented.
+	 */
+	if (*pos >= ap_config_size)
+		len = 0;
+	else
+		len = min_t(size_t, ap_config_size - *pos, len);
+
+	/* If we've reached an EOF condition, let the caller know */
+	if (len == 0) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return 0;
+	}
+
+	/*
+	 * Allocate ap_config and copy the content of the object caching the
+	 * AP configuration data between calls to it so we can give up the
+	 * global mdevs_lock while copying the data to userspace.
+	 */
+	ap_config = kzalloc(ap_config_size, GFP_KERNEL_ACCOUNT);
+	if (!ap_config) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return -ENOMEM;
+	}
+
+	memcpy(ap_config, matrix_mdev->mig_data->stop_copy_mig_file.ap_config,
+	       ap_config_size);
+
+	/*
+	 * Give up the global mdevs_lock while copying data to the user; it
+	 * might be a long running operation and we don't want to prevent access
+	 * to other mdevs for an inordinate amount of time.
+	 */
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	if (copy_to_user(buf, (char *)ap_config + *pos, len)) {
+		kfree(ap_config);
+		return -EFAULT;
+	}
+
+	kfree(ap_config);
+	*pos += len;
+
+	return len;
+}
+
+static const struct file_operations vfio_ap_stop_copy_fops = {
+	.owner = THIS_MODULE,
+	.read = vfio_ap_stop_copy_read,
+	.compat_ioctl = compat_ptr_ioctl,
+	.release = vfio_ap_release_mig_file,
+};
+
+static struct file *vfio_ap_open_file_stream(struct ap_matrix_mdev *matrix_mdev,
+					     const struct file_operations *fops,
+					     int flags)
+{
+	struct file *filp;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	filp = anon_inode_getfile("vfio_ap_mig_file", fops, matrix_mdev, flags);
+	if (!IS_ERR(filp))
+		stream_open(filp->f_inode, filp);
+
+	return filp;
+}
+
+static int validate_resuming_write_parms(struct file *filp,
+					 size_t len, loff_t *pos)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	loff_t total_len;
+	int ret;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	matrix_mdev = filp->private_data;
+	if (!matrix_mdev || !matrix_mdev->mig_data) {
+		ret = -ENODEV;
+		goto done;
+	}
+
+	if (filp != matrix_mdev->mig_data->resuming_mig_file.filp) {
+		ret = -ENXIO;
+		goto done;
+	}
+
+	if (*pos < 0) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (check_add_overflow((loff_t)len, *pos, &total_len)) {
+		ret = -ERANGE;
+		goto done;
+	}
+
+	/*
+	 * If the ap_config has not yet been allocated and the file position
+	 * indicates this is not the first write, or the ap_config has been allocated
+	 * but the file position indicates this is the first write, then this is an
+	 * error condition.
+	 */
+	if ((!matrix_mdev->mig_data->resuming_mig_file.ap_config && *pos != 0) ||
+	    (matrix_mdev->mig_data->resuming_mig_file.ap_config && *pos == 0)) {
+		ret = -EFAULT;
+
+		goto done;
+	}
+
+	ret = 0;
+
+done:
+	return ret;
+}
+
+static ssize_t calculate_ap_config_size(unsigned int num_queues)
+{
+	size_t qinfo_size;
+
+	if (num_queues > MAX_AP_QUEUES)
+		return -EINVAL;
+
+	qinfo_size = num_queues * sizeof(struct vfio_ap_queue_info);
+	return qinfo_size + sizeof(struct vfio_ap_config);
+}
+
+/**
+ * allocate_ap_config:
+ *
+ * Allocate storage for the source guest's AP configuration data sent from
+ * userspace.
+ *
+ * @ap_config:	The location in which to store the pointer to the storage
+ *		allocated for the AP configuration data.
+ * @buf:	The userspace buffer containing some or all of the source
+ *		guest's AP configuration data
+ * @len:	The number of bytes of data to copy from @buf
+ *
+ * Returns:	The number of bytes of storage allocated for the config data or
+ *		an error:
+ *
+ *
+ *		-EIO: failed to copy data from @buf
+ *		-EINVAL: the number of queues specified exceeds the max allowed
+ *		-ENOMEM: the allocation of storage failed
+ */
+static ssize_t allocate_ap_config(struct vfio_ap_config **ap_config,
+				  const char __user *buf, size_t len)
+{
+	struct vfio_ap_config tmp_ap_config;
+	ssize_t config_size;
+	size_t copy_size;
+
+	/*
+	 * If the length of the data sent exceeds the size of the vfio_ap_config
+	 * structure, then we will copy enough data from userspace to get the
+	 * number of queues which we can use to allocate enough space all of
+	 * the queue information.
+	 */
+	copy_size = min(len, sizeof(tmp_ap_config));
+
+	if (copy_from_user(&tmp_ap_config, buf, copy_size))
+		return -EIO;
+
+	/*
+	 * If the length of data sent includes the number of queues
+	 * in the AP configuration, then calculate its size; otherwise
+	 * set config_size to the length of data sent.
+	 */
+	if (len >= sizeof(struct vfio_ap_config)) {
+		config_size = calculate_ap_config_size(tmp_ap_config.num_queues);
+
+		/* If the calculation returned an error */
+		if (config_size < 0)
+			return config_size;
+	} else {
+		config_size = len;
+	}
+
+	*ap_config = kzalloc(config_size, GFP_KERNEL_ACCOUNT);
+	if (!*ap_config)
+		return -ENOMEM;
+
+	return config_size;
+}
+
+static ssize_t reallocate_ap_config(struct vfio_ap_migration_file *mig_file,
+				    struct vfio_ap_config **new_ap_config,
+				    size_t len)
+{
+	struct vfio_ap_config *cur_ap_config;
+	unsigned long cur_cfg_sz;
+	ssize_t new_cfg_sz;
+
+	cur_ap_config = mig_file->ap_config;
+	cur_cfg_sz = mig_file->config_sz;
+
+	/*
+	 * If the current configuration size is greater than the
+	 * size of a vfio_ap_config structure (i.e., contains the num_queues
+	 * field), then there should already be enough storage allocated
+	 * to store the source guest's AP configuration. Let's verify that the
+	 * amount of storage allocated is what we expect based on the number of
+	 * vfio_ap_queue_info objects that must be stored.
+	 */
+	if (cur_cfg_sz >= sizeof(struct vfio_ap_config)) {
+		new_cfg_sz = calculate_ap_config_size(cur_ap_config->num_queues);
+		if (cur_cfg_sz != new_cfg_sz)
+			return -EINVAL;
+	} else {
+		if (check_add_overflow(cur_cfg_sz, len, &new_cfg_sz))
+			return -ERANGE;
+	}
+
+	*new_ap_config = kzalloc(new_cfg_sz, GFP_KERNEL_ACCOUNT);
+	if (!*new_ap_config)
+		return -ENOMEM;
+
+	memcpy(*new_ap_config, cur_ap_config, cur_cfg_sz);
+
+	return new_cfg_sz;
+}
+
+/**
+ * qdev_is_bound_to_vfio_ap:
+ *
+ * Query to determine whether a queue with the specified APQN is available on
+ * the host system and bound to the vfio_ap device driver.
+ *
+ * @apqn: The APQN of the queue device being queried
+ *
+ * Returns: True if there is a queue device with the specified @apqn installed
+ *	    in the system and is bound to the vfio_ap device driver; otherwise,
+ *	    returns false.
+ */
+static bool qdev_is_bound_to_vfio_ap(unsigned int apqn)
+{
+	struct ap_queue *queue;
+	bool is_bound = true;
+
+	queue = ap_get_qdev(apqn);
+	if (!queue)
+		return false;
+
+	if (queue->ap_dev.device.driver != &matrix_dev->vfio_ap_drv->driver)
+		is_bound = false;
+
+	put_device(&queue->ap_dev.device);
+
+	return is_bound;
+}
+
+/**
+ * queues_available:
+ *
+ * Query whether each queue from the source guest's AP configuration is
+ * available and bound to the vfio_ap device driver; if not, log an error
+ * message.
+ *
+ * @mdev_name:	   The mdev name to use in error messages
+ * @source_config: The object specifying the source guest's AP configuration
+ *
+ * Returns: true if each queue identified in @source_config is available and
+ *	    bound to the vfio_ap device driver; otherwise, returns false.
+ */
+static bool queues_available(const char *mdev_name,
+			     struct vfio_ap_config *source_config)
+{
+	unsigned long apqn;
+	bool ret = true;
+
+	for (int i = 0; i < source_config->num_queues; i++) {
+		apqn = source_config->qinfo[i].apqn;
+
+		/*
+		 * Find the queue device bound to the vfio_ap device driver. If it is
+		 * not found, log an error and continue so users see all problems
+		 * at once, not one-at-a-time through retries of the migration.
+		 */
+		if (!qdev_is_bound_to_vfio_ap(apqn)) {
+			pr_err("vfio_ap_mdev %s: Queue %02lx.%04lx not available to vfio_ap driver on target host\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+			ret = false;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * control_domains_available
+ *
+ * Query whether each control domain specified in the source guest's AP
+ * configuration is installed in the host system.
+ *
+ * @mdev_name:		The name of the mdev to use when logging messages
+ * @source_config:	The object specifying the source guest's AP config
+ *
+ * Returns:	True if each control domain is installed; otherwise, logs an
+ *		error message for each unavailable control domain and returns
+ *		false.
+ */
+static bool control_domains_available(const char *mdev_name,
+				      struct vfio_ap_config *source_config)
+{
+	unsigned long domain_num;
+	bool available = true;
+
+	for_each_set_bit_inv(domain_num, (unsigned long *)source_config->adm,
+			     AP_DOMAINS) {
+		if (!ap_test_config_ctrl_domain(domain_num)) {
+			pr_err("vfio_ap_mdev: %s: Control domain %04lx not available on the destination host",
+			       mdev_name, domain_num);
+			available = false;
+		}
+	}
+
+	return available;
+}
+
+static void report_facilities_compatibility(const char *mdev_name,
+					    unsigned long apqn,
+					    struct ap_tapq_hwinfo *src_hwinfo,
+					    struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->apsc != target_hwinfo->apsc) {
+		if (src_hwinfo->apsc) {
+			pr_err("vfio_ap_mdev %s: APSC facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: APSC facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: APSC facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s APSC facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->mex4k != target_hwinfo->mex4k) {
+		if (src_hwinfo->mex4k) {
+			pr_err("vfio_ap_mdev %s: mex4k facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: mex4k facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: mex4k facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: mex4k facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->crt4k != target_hwinfo->crt4k) {
+		if (src_hwinfo->crt4k) {
+			pr_err("vfio_ap_mdev %s: crt4k facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: crt4k facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: crt4k facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: crt4k facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_mode_compatibility(const char *mdev_name,
+				      unsigned long apqn,
+				      struct ap_tapq_hwinfo *src_hwinfo,
+				      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->cca != target_hwinfo->cca) {
+		if (src_hwinfo->cca) {
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode  facility not installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Coprocessor-mode  facility installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->accel != target_hwinfo->accel) {
+		if (src_hwinfo->accel) {
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility not installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility not installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Accelerator-mode facility installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+
+	if (src_hwinfo->ep11 != target_hwinfo->ep11) {
+		if (src_hwinfo->ep11) {
+			pr_err("vfio_ap_mdev %s: XCP-mode facility installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: XCP-mode facility not installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: XCP-mode facility not installed source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: XCP-mode facility installed target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_apxa_compatibility(const char *mdev_name,
+				      unsigned long apqn,
+				      struct ap_tapq_hwinfo *src_hwinfo,
+				      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->apxa != target_hwinfo->apxa) {
+		if (src_hwinfo->apxa) {
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility not installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility not installed in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: AP-extended-addressing (APXA) facility installed in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_slcf_compatibility(const char *mdev_name,
+				      unsigned long apqn,
+				      struct ap_tapq_hwinfo *src_hwinfo,
+				      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->slcf != target_hwinfo->slcf) {
+		if (src_hwinfo->slcf) {
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) available in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) not available in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		} else {
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) not available in source queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+			pr_err("vfio_ap_mdev %s: Stateless-command-filtering (SLCF) available in target queue %02lx.%04lx\n",
+			       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+		}
+	}
+}
+
+static void report_bs_compatibility(const char *mdev_name,
+				    unsigned long apqn,
+				    struct ap_tapq_hwinfo *src_hwinfo,
+				    struct ap_tapq_hwinfo *target_hwinfo)
+{
+	/*
+	 * The BS field on both the source and destination must be 0, so if one
+	 * of them is not, then report an error.
+	 */
+	if (src_hwinfo->bs || target_hwinfo->bs) {
+		pr_err("vfio_ap_mdev %s: Bind/associate state for source (%01x) and target (%01x) queue %02lx.%04lx must be 0\n",
+		       mdev_name, src_hwinfo->bs, target_hwinfo->bs,
+		       AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+	}
+}
+
+static void report_aptype_compatibility(const char *mdev_name,
+					unsigned long apqn,
+					struct ap_tapq_hwinfo *src_hwinfo,
+					struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (src_hwinfo->at > target_hwinfo->at) {
+		pr_err("vfio_ap_mdev %s: AP type of source (%02x) not compatible with target (%02x)\n",
+		       mdev_name, src_hwinfo->at, target_hwinfo->at);
+	}
+}
+
+static bool classes_compatible(struct ap_tapq_hwinfo *src_hwinfo,
+			       struct ap_tapq_hwinfo *target_hwinfo)
+{
+	unsigned long src_native, target_native;
+
+	src_native = src_hwinfo->class & CLASSIFICATION_NATIVE_FCN_MASK;
+	target_native = target_hwinfo->class & CLASSIFICATION_NATIVE_FCN_MASK;
+
+	/*
+	 * If the source queue has full native card function and the
+	 * target queue has only stateless functions available, then
+	 * there may be instructions that will not execute on the
+	 * target queue. This shall be reported as an error.
+	 *
+	 * If the source queue has only stateless card functions and the
+	 * target queue has full native card function available, then
+	 * we are okay because the target queue can run all stateless card
+	 * functions.
+	 */
+	return (src_native != target_native) ? !src_native : true;
+}
+
+static void report_class_compatibility(const char *mdev_name,
+				       unsigned long apqn,
+				       struct ap_tapq_hwinfo *src_hwinfo,
+				       struct ap_tapq_hwinfo *target_hwinfo)
+{
+	if (!classes_compatible(src_hwinfo, target_hwinfo)) {
+		pr_err("vfio_ap_mdev %s: Full native card function available on source queue %02lx.%04lx\n",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+		pr_err("vfio_ap_mdev %s: Only stateless functions available on target queue %02lx.%04lx\n",
+		       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+	}
+}
+
+/*
+ * Log a device error reporting that migration failed due to queue
+ * incompatibilities followed by a device error for each incompatible feature.
+ */
+static void report_qinfo_incompatibilities(const char *mdev_name,
+					   unsigned long apqn,
+					   struct ap_tapq_hwinfo *src_hwinfo,
+					   struct ap_tapq_hwinfo *target_hwinfo)
+{
+	pr_err("vfio_ap_mdev %s: Migration failed: Source and target queue (%02lx.%04lx) not compatible\n",
+	       mdev_name, AP_QID_CARD(apqn), AP_QID_QUEUE(apqn));
+
+	report_facilities_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_mode_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_apxa_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_slcf_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_aptype_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_bs_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+	report_class_compatibility(mdev_name, apqn, src_hwinfo, target_hwinfo);
+}
+
+/**
+ * queue_hardware_info_is_compatible:
+ *
+ * Verify whether the hardware information for a source queue is compatible with
+ * the hardware info for the corresponding queue on this system.
+ *
+ * In order to be compatible, the hardware information for each queue must
+ * meet the following requirements:
+ *
+ * 1. The hardware facilities bits much match
+ * 2. The AP type of the source queue must be the same as or older than that
+ *    of the target queue (target is backwards compatible)
+ * 3. The classification bits must indicate:
+ *    - Both queues have full native card function or both have stateless
+ *      functions available
+ *    - If the classification bits don't match, then the only acceptable
+ *      configuration is stateless functions for the source queue and
+ *      full native function for the target queue
+ * 4. The BS bits for both queues must be 0 (Queue usable for all messages
+ *    supported by the adapter)
+ *
+ * @mdev_name:	The mdev name to use in error messages
+ * @apqn:	The APQN for the queues
+ * @src_hwinfo: The hardware info for the source queue
+ * @target_hwinfo: The hardware info for the corresponding queue on this system
+ *
+ * Returns: true if the hardware info for the two queues is compatible;
+ *          otherwise, returns false.
+ */
+static bool queue_hardware_info_is_compatible(const char *mdev_name,
+					      unsigned long apqn,
+					      struct ap_tapq_hwinfo *src_hwinfo,
+					      struct ap_tapq_hwinfo *target_hwinfo)
+{
+	unsigned long src_bits, target_bits;
+
+	src_bits = src_hwinfo->value & QINFO_DATA_MASK;
+	target_bits = target_hwinfo->value & QINFO_DATA_MASK;
+
+	/* If all bits match the queues are compatible */
+	if (src_bits == target_bits &&
+	    (src_hwinfo->bs == 0 && target_hwinfo->bs == 0))
+		return true;
+
+	if (src_hwinfo->apsc  == target_hwinfo->apsc     &&
+	    src_hwinfo->mex4k == target_hwinfo->mex4k    &&
+	    src_hwinfo->crt4k == target_hwinfo->crt4k    &&
+	    src_hwinfo->cca   == target_hwinfo->cca      &&
+	    src_hwinfo->accel == target_hwinfo->accel    &&
+	    src_hwinfo->ep11  == target_hwinfo->ep11     &&
+	    src_hwinfo->slcf  == target_hwinfo->slcf     &&
+	    src_hwinfo->apxa  == target_hwinfo->apxa     &&
+	    src_hwinfo->at    <= target_hwinfo->at       &&
+	    classes_compatible(src_hwinfo, target_hwinfo) &&
+	    (src_hwinfo->bs == 0 && target_hwinfo->bs == 0))
+		return true;
+
+	report_qinfo_incompatibilities(mdev_name, apqn, src_hwinfo, target_hwinfo);
+
+	return false;
+}
+
+/**
+ * verify_ap_configs_are_compatible:
+ *
+ * Verifies that the queues in the source guest's AP configuration are
+ * compatible with the corresponding queues on this system.
+ *
+ * @mdev_name:	   The mdev name to use in error messages
+ * @source_config: The object specifying the source guest's AP configuration
+ *
+ * Returns: an error indicating either a failure to retrieve a queue's
+ *			hardware information or one or more source queues are not
+ *			compatible with the corresponding queue on this system; otherwise,
+ *			returns zero to indicate compatibility.
+ */
+static int verify_ap_configs_are_compatible(const char *mdev_name,
+					    struct vfio_ap_config *source_config)
+{
+	struct ap_tapq_hwinfo src_hwinfo, dest_hwinfo;
+	unsigned long apqn;
+	int ret = 0, rc;
+
+	for (int i = 0; i < source_config->num_queues; i++) {
+		apqn = source_config->qinfo[i].apqn;
+
+		/*
+		 * If we can't get the hardware info for a particular queue, then let's
+		 * capture the function return code and continue so we can log all
+		 * errors to aid in debugging of migration.
+		 */
+		rc = get_hardware_info_for_queue(mdev_name, &dest_hwinfo, apqn);
+		if (rc) {
+			ret = rc;
+			continue;
+		}
+
+		src_hwinfo.value =  source_config->qinfo[i].data;
+
+		if (!queue_hardware_info_is_compatible(mdev_name, apqn,
+						       &src_hwinfo,
+						       &dest_hwinfo))
+			ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int do_post_copy_validation(const char *mdev_name,
+				   struct vfio_ap_config *source_config)
+{
+	if (!queues_available(mdev_name, source_config))
+		return -ENODEV;
+
+	if (!control_domains_available(mdev_name, source_config))
+		return -ENODEV;
+
+	return verify_ap_configs_are_compatible(mdev_name, source_config);
+}
+
+/**
+ * setup_ap_matrix_from_ap_config:
+ *
+ * Set the bits corresponding to the adapters, domains and control domains
+ * from the source guest's AP configuration into an ap_matrix object to be
+ * used to update the destination guest to run on this host.
+ *
+ * @ap_config:		The source guest's AP configuration
+ * @guest_matrix:	The object to be used to update the destination guest's
+ *			AP configuration
+ */
+static void setup_ap_matrix_from_ap_config(struct vfio_ap_config *ap_config,
+					   struct ap_matrix *guest_matrix)
+{
+	struct ap_config_info host_config_info = { 0 };
+	unsigned long apid, apqi, *guest_adm;
+	struct vfio_ap_queue_info qinfo;
+
+	ap_qci(&host_config_info);
+	vfio_ap_matrix_init(&host_config_info, guest_matrix);
+
+	for (int i = 0; i < ap_config->num_queues; i++) {
+		qinfo = ap_config->qinfo[i];
+		apid = AP_QID_CARD(qinfo.apqn);
+		apqi = AP_QID_QUEUE(qinfo.apqn);
+
+		if (!test_bit_inv(apid, guest_matrix->apm))
+			set_bit_inv(apid, guest_matrix->apm);
+		if (!test_bit_inv(apqi, guest_matrix->aqm))
+			set_bit_inv(apqi, guest_matrix->aqm);
+	}
+
+	guest_adm = (unsigned long *)ap_config->adm;
+	for_each_set_bit_inv(apqi, guest_adm, AP_DOMAINS) {
+		if (!test_bit_inv(apqi, guest_matrix->adm))
+			set_bit_inv(apqi, guest_matrix->adm);
+	}
+}
+
+static ssize_t vfio_ap_resuming_write(struct file *filp, const char __user *buf,
+				      size_t len, loff_t *pos)
+{
+	struct vfio_ap_migration_data *mig_data;
+	struct ap_matrix_mdev *matrix_mdev;
+	struct vfio_ap_config *ap_config;
+	struct ap_matrix guest_matrix;
+	ssize_t ret = 0, cfg_sz;
+	const char *mdev_name;
+
+	/*
+	 * When userspace calls write() with an explicit offset (pwrite), pos is
+	 * non-NULL and the function rejects it with -ESPIPE (illegal seek). For
+	 * normal write() calls, pos is NULL, so we'll use the file's internal
+	 * position filp->f_pos
+	 */
+	if (pos)
+		return -ESPIPE;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	pos = &filp->f_pos;
+
+	ret = validate_resuming_write_parms(filp, len, pos);
+	if (ret) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	matrix_mdev = filp->private_data;
+	mig_data = matrix_mdev->mig_data;
+	mdev_name = dev_name(matrix_mdev->vdev.dev);
+
+	/*
+	 * If this is the first write operation, then allocate storage for the
+	 * AP configuration information; otherwise, reallocate the
+	 * struct vfio_ap_config object used to store the AP configuration data
+	 * sent from userspace.
+	 */
+	if (*pos == 0) {
+		ret = allocate_ap_config(&ap_config, buf, len);
+
+		/* If the allocation failed, we'll return the error */
+		if (ret < 0) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			return ret;
+		}
+
+		cfg_sz = ret;
+	} else {
+		ret = reallocate_ap_config(&mig_data->resuming_mig_file,
+					   &ap_config, len);
+		if (ret < 0) {
+			mutex_unlock(&matrix_dev->mdevs_lock);
+			return ret;
+		}
+
+		cfg_sz = ret;
+	}
+
+	if (*pos + len > cfg_sz) {
+		ret = -EIO;
+		kfree(ap_config);
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ret;
+	}
+
+	/*
+	 * We don't want to lock all mdevs while copying data from userspace so
+	 * we don't block all other mdevs in case the I/O takes a long time.
+	 * From here on out we don't need this lock because we are not
+	 * accessing the matrix_mdev until we need to get the update
+	 * locks to set the new destination guest's AP configuration in which
+	 * case this lock will be taken then.
+	 */
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	if (copy_from_user((char *)ap_config + *pos, buf, len)) {
+		ret = -EIO;
+		kfree(ap_config);
+
+		return ret;
+	}
+
+	/* Check if we've completed writing the entire configuration */
+	if (*pos + len == cfg_sz) {
+		/* Acquire locks required to update the guest's AP config */
+		mutex_lock(&ap_attr_mutex);
+		get_update_locks_for_mdev(matrix_mdev);
+
+		/*
+		 * Verify the device wasn't closed while mdevs_lock was dropped
+		 * for the copy_from_user above. get_update_locks_for_mdev()
+		 * reacquires mdevs_lock, so the check is safe here.
+		 */
+		if (!matrix_mdev->mig_data) {
+			release_update_locks_for_mdev(matrix_mdev);
+			mutex_unlock(&ap_attr_mutex);
+			kfree(ap_config);
+			return -ENODEV;
+		}
+
+		ret = do_post_copy_validation(mdev_name, ap_config);
+		if (ret < 0) {
+			kfree(ap_config);
+			release_update_locks_for_mdev(matrix_mdev);
+			mutex_unlock(&ap_attr_mutex);
+			return ret;
+		}
+
+		setup_ap_matrix_from_ap_config(ap_config, &guest_matrix);
+
+		ret = vfio_ap_set_new_guest_config(matrix_mdev, &guest_matrix);
+
+		release_update_locks_for_mdev(matrix_mdev);
+		mutex_unlock(&ap_attr_mutex);
+
+		if (ret) {
+			kfree(ap_config);
+			return ret;
+		}
+	}
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	kfree(mig_data->resuming_mig_file.ap_config);
+	mig_data->resuming_mig_file.ap_config = ap_config;
+	mig_data->resuming_mig_file.config_sz = cfg_sz;
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	ret = len;
+	*pos += len;
+
+	return ret;
+}
+
+static const struct file_operations vfio_ap_resume_fops = {
+	.owner = THIS_MODULE,
+	.write = vfio_ap_resuming_write,
+	.release = vfio_ap_release_mig_file,
+};
+
+static struct file *vfio_ap_resuming_init(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+	struct file *filp;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = matrix_mdev->mig_data;
+	filp = vfio_ap_open_file_stream(matrix_mdev, &vfio_ap_resume_fops, O_WRONLY);
+
+	return filp;
+}
+
+static struct file *
+vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,
+			    enum vfio_device_mig_state new_state)
+{
+	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);
+
+	/*
+	 * 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) {
+		struct file *filp = vfio_ap_open_file_stream(matrix_mdev,
+							     &vfio_ap_stop_copy_fops,
+							     O_RDONLY);
+		if (IS_ERR(filp))
+			return ERR_CAST(filp);
+
+		mig_data->stop_copy_mig_file.filp = filp;
+
+		return filp;
+	}
+
+	/*
+	 * 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) {
+		struct file *filp = vfio_ap_resuming_init(matrix_mdev);
+
+		if (IS_ERR(filp))
+			return ERR_CAST(filp);
+
+		mig_data->resuming_mig_file.filp = filp;
+		return filp;
+	}
+
+	/*
+	 * Terminates the data transfer session of the vfio-ap device state
+	 * between the source and target hosts. Since the vfio-ap device does
+	 * not virtualize a DMA device, there is no internal device state to
+	 * incorporate into the vfio-ap device on the target.
+	 */
+	if ((cur_state == VFIO_DEVICE_STATE_RESUMING &&
+	     new_state == VFIO_DEVICE_STATE_STOP) ||
+	    (cur_state == VFIO_DEVICE_STATE_STOP_COPY &&
+	     new_state == VFIO_DEVICE_STATE_STOP)) {
+		return NULL;
+	}
+
+	/*
+	 * These states indicate migration has either not been initiated or
+	 * has completed and the vfio-ap device is operating normally.Since the
+	 * vfio-ap device does not virtualize a DMA device, there is no internal
+	 * device state to incorporate into the vfio-ap device on the target.
+	 */
+	if ((cur_state == VFIO_DEVICE_STATE_STOP &&
+	     new_state == VFIO_DEVICE_STATE_RUNNING) ||
+	    (cur_state == VFIO_DEVICE_STATE_RUNNING &&
+	     new_state == VFIO_DEVICE_STATE_STOP)) {
+		return NULL;
+	}
+
+	/* 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;
+
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	if (ap_is_se_guest()) {
+		dev_err_once(matrix_mdev->vdev.dev,
+			     "Migration not allowed from or to a Secure Execution guest\n");
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		return ERR_PTR(-EPERM);
+	}
+
+	mig_data = matrix_mdev->mig_data;
+
+	/*
+	 * The mig_data pointer is set in the vfio_ap_init_migration_data
+	 * function which is called when the vfio-ap device fd is opened.
+	 * Since the implicit pre-open state is RUNNING, a request to set
+	 * RUNNING is a no-op. Any other state transition is invalid before
+	 * open_device.
+	 */
+	if (!mig_data) {
+		mutex_unlock(&matrix_dev->mdevs_lock);
+		if (new_state == VFIO_DEVICE_STATE_RUNNING)
+			return NULL;
+		return ERR_PTR(-ENODEV);
+	}
+
+	dev_dbg(vdev->dev, "%s -> %d\n", __func__, new_state);
+
+	while (mig_data->mig_state != VFIO_DEVICE_STATE_ERROR &&
+	       mig_data->mig_state != new_state) {
+		ret = vfio_mig_get_next_state(vdev, mig_data->mig_state,
+					      new_state, &next_state);
+		if (ret) {
+			filp = ERR_PTR(ret);
+			break;
+		}
+
+		filp = vfio_ap_transition_to_state(matrix_mdev, next_state);
+		if (IS_ERR(filp))
+			break;
+
+		mig_data->mig_state = next_state;
+
+		if (WARN_ON(filp && new_state != next_state)) {
+			fput(filp);
+			filp = ERR_PTR(-EINVAL);
+			break;
+		}
+	}
+
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	return filp;
+}
+
+static int vfio_ap_get_state(struct vfio_device *vdev,
+			     enum vfio_device_mig_state  *current_state)
+{
+	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)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+	size_t qinfo_sz;
+	int num_queues;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	num_queues = vfio_ap_mdev_get_num_queues(&matrix_mdev->shadow_apcb);
+	qinfo_sz = num_queues * sizeof(struct vfio_ap_queue_info);
+	*stop_copy_length = qinfo_sz + sizeof(struct vfio_ap_config);
+	mutex_unlock(&matrix_dev->mdevs_lock);
+
+	return 0;
+}
+
+static const struct vfio_migration_ops vfio_ap_migration_ops = {
+	.migration_set_state = vfio_ap_set_state,
+	.migration_get_state = vfio_ap_get_state,
+	.migration_get_data_size = vfio_ap_get_data_size,
+};
+
+/**
+ * vfio_ap_init_migrations_capabilities - initialize migration capabilities
+ *
+ * @matrix_mdev: pointer to object containing the mdev state
+ */
+void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev)
+{
+	/* Live guest migration is not supported for SE guests */
+	if (ap_is_se_guest())
+		return;
+
+	matrix_mdev->vdev.migration_flags = VFIO_MIGRATION_STOP_COPY;
+	matrix_mdev->vdev.mig_ops = &vfio_ap_migration_ops;
+}
+
+/**
+ * vfio_ap_init_migration_data - initialize migration data and functions
+ *
+ * @matrix_mdev: pointer to object containing the mdev state
+ *
+ * Return: zero if initialization is successful; otherwise, returns a error.
+ */
+int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = kzalloc_obj(struct vfio_ap_migration_data, GFP_KERNEL);
+	if (!mig_data)
+		return -ENOMEM;
+
+	mig_data->mig_state = VFIO_DEVICE_STATE_RUNNING;
+	matrix_mdev->mig_data = mig_data;
+
+	return 0;
+}
+
+/**
+ * vfio_ap_release_migration_data: reclaim private migration data
+ *
+ * @vdev: pointer to the mdev
+ */
+void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev)
+{
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (!matrix_mdev->mig_data)
+		return;
+
+	kfree(matrix_mdev->mig_data->resuming_mig_file.ap_config);
+	kfree(matrix_mdev->mig_data->stop_copy_mig_file.ap_config);
+	kfree(matrix_mdev->mig_data);
+	matrix_mdev->mig_data = NULL;
+}
+
+static void vfio_ap_release_mig_files(struct ap_matrix_mdev *matrix_mdev)
+{
+	struct vfio_ap_migration_data *mig_data;
+
+	/*
+	 * The fput call does not call .release synchronously while the
+	 * mdevs_lock mutex is held, so there is no problem with incurring a
+	 * deadlock situation if fput is executed in this function.
+	 */
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	mig_data = matrix_mdev->mig_data;
+	if (!mig_data)
+		return;
+
+	if (mig_data->stop_copy_mig_file.filp) {
+		fput(mig_data->stop_copy_mig_file.filp);
+		mig_data->stop_copy_mig_file.filp = NULL;
+	}
+
+	kfree(mig_data->stop_copy_mig_file.ap_config);
+	mig_data->stop_copy_mig_file.ap_config = NULL;
+	mig_data->stop_copy_mig_file.config_sz = 0;
+
+	if (mig_data->resuming_mig_file.filp) {
+		fput(mig_data->resuming_mig_file.filp);
+		mig_data->resuming_mig_file.filp = NULL;
+	}
+
+	kfree(mig_data->resuming_mig_file.ap_config);
+	mig_data->resuming_mig_file.ap_config = NULL;
+	mig_data->resuming_mig_file.config_sz = 0;
+}
+
+/**
+ * vfio_ap_reset_migration_state - Reset the vfio-ap migration state
+ *
+ * @matrix_mdev: pointer to the object maintaining the vfio-ap device state
+ *
+ * Called during VFIO_DEVICE_RESET to clean up any active migration
+ * state and reset the device to RUNNING state as required by the VFIO
+ * migration specification.
+ */
+void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev)
+{
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	if (!matrix_mdev->mig_data)
+		return;
+
+	vfio_ap_release_mig_files(matrix_mdev);
+	matrix_mdev->mig_data->mig_state = VFIO_DEVICE_STATE_RUNNING;
+}
diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 44b3a1dcc1b365..454bb9fe197168 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -81,53 +81,6 @@ static inline void release_update_locks_for_kvm(struct kvm *kvm)
 	mutex_unlock(&matrix_dev->guests_lock);
 }
 
-/**
- * get_update_locks_for_mdev: Acquire the locks required to dynamically update a
- *			      KVM guest's APCB in the proper order.
- *
- * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
- *		 configuration data to use to update a KVM guest's APCB.
- *
- * The proper locking order is:
- * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
- *			       guest's APCB.
- * 2. matrix_mdev->kvm->lock:  required to update a guest's APCB
- * 3. matrix_dev->mdevs_lock:  required to access data stored in a matrix_mdev
- *
- * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
- *	 lock will not be taken.
- */
-static inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
-{
-	mutex_lock(&matrix_dev->guests_lock);
-	if (matrix_mdev && matrix_mdev->kvm)
-		mutex_lock(&matrix_mdev->kvm->lock);
-	mutex_lock(&matrix_dev->mdevs_lock);
-}
-
-/**
- * release_update_locks_for_mdev: Release the locks used to dynamically update a
- *				  KVM guest's APCB in the proper order.
- *
- * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
- *		 configuration data to use to update a KVM guest's APCB.
- *
- * The proper unlocking order is:
- * 1. matrix_dev->mdevs_lock
- * 2. matrix_mdev->kvm->lock
- * 3. matrix_dev->guests_lock
- *
- * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
- *	 lock will not be released.
- */
-static inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
-{
-	mutex_unlock(&matrix_dev->mdevs_lock);
-	if (matrix_mdev && matrix_mdev->kvm)
-		mutex_unlock(&matrix_mdev->kvm->lock);
-	mutex_unlock(&matrix_dev->guests_lock);
-}
-
 /**
  * get_update_locks_by_apqn: Find the mdev to which an APQN is assigned and
  *			     acquire the locks required to update the APCB of
@@ -642,8 +595,7 @@ static int handle_pqap(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-static void vfio_ap_matrix_init(struct ap_config_info *info,
-				struct ap_matrix *matrix)
+void vfio_ap_matrix_init(struct ap_config_info *info, struct ap_matrix *matrix)
 {
 	matrix->apm_max = info->apxa ? info->na : 63;
 	matrix->aqm_max = info->apxa ? info->nd : 15;
@@ -775,18 +727,30 @@ static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev,
 
 static int vfio_ap_mdev_init_dev(struct vfio_device *vdev)
 {
-	struct ap_matrix_mdev *matrix_mdev =
-		container_of(vdev, struct ap_matrix_mdev, vdev);
+	struct ap_matrix_mdev *matrix_mdev;
 
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
 	matrix_mdev->mdev = to_mdev_device(vdev->dev);
 	vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
 	matrix_mdev->pqap_hook = handle_pqap;
 	vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb);
 	hash_init(matrix_mdev->qtable.queues);
+	mutex_unlock(&matrix_dev->mdevs_lock);
 
 	return 0;
 }
 
+static void vfio_ap_mdev_release_dev(struct vfio_device *vdev)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+
+	mutex_lock(&matrix_dev->mdevs_lock);
+	matrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);
+	vfio_ap_release_migration_data(matrix_mdev);
+	mutex_unlock(&matrix_dev->mdevs_lock);
+}
+
 static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 {
 	struct ap_matrix_mdev *matrix_mdev;
@@ -797,18 +761,29 @@ static int vfio_ap_mdev_probe(struct mdev_device *mdev)
 	if (IS_ERR(matrix_mdev))
 		return PTR_ERR(matrix_mdev);
 
+	mutex_lock(&matrix_dev->mdevs_lock);
+
+	/*
+	 * Migration capabilities must be initialized before calling
+	 * vfio_register_emulated_iommu_dev; otherwise, the VFIO core
+	 * will see mig_ops as NULL during the registration. This could
+	 * prevent the VFIO core from properly setting up migration
+	 * infrastructure like debugfs entries.
+	 */
+	vfio_ap_init_migration_capabilities(matrix_mdev);
+
 	ret = vfio_register_emulated_iommu_dev(&matrix_mdev->vdev);
 	if (ret)
 		goto err_put_vdev;
 	matrix_mdev->req_trigger = NULL;
 	matrix_mdev->cfg_chg_trigger = NULL;
 	dev_set_drvdata(&mdev->dev, matrix_mdev);
-	mutex_lock(&matrix_dev->mdevs_lock);
 	list_add(&matrix_mdev->node, &matrix_dev->mdev_list);
 	mutex_unlock(&matrix_dev->mdevs_lock);
 	return 0;
 
 err_put_vdev:
+	mutex_unlock(&matrix_dev->mdevs_lock);
 	vfio_put_device(&matrix_mdev->vdev);
 	return ret;
 }
@@ -852,7 +827,7 @@ static void vfio_ap_mdev_unlink_fr_queues(struct ap_matrix_mdev *matrix_mdev)
 			q = vfio_ap_mdev_get_queue(matrix_mdev,
 						   AP_MKQID(apid, apqi));
 			if (q)
-				q->matrix_mdev = NULL;
+				vfio_ap_mdev_link_queue(matrix_mdev, q);
 		}
 	}
 }
@@ -991,19 +966,20 @@ static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev,
 	unsigned long apqi;
 
 	for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, AP_DOMAINS)
-		vfio_ap_mdev_link_apqn(matrix_mdev,
-				       AP_MKQID(apid, apqi));
+		vfio_ap_mdev_link_apqn(matrix_mdev, AP_MKQID(apid, apqi));
 }
 
-static void collect_queues_to_reset(struct ap_matrix_mdev *matrix_mdev,
-				    unsigned long apid,
-				    struct list_head *qlist)
+static void collect_queues_by_apid(struct ap_matrix_mdev *matrix_mdev,
+				   unsigned long apid,
+				   struct list_head *qlist)
 {
 	struct vfio_ap_queue *q;
 	unsigned long  apqi;
 
 	for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS) {
-		q = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi));
+		q = matrix_mdev ?
+				vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)) :
+				vfio_ap_find_queue(AP_MKQID(apid, apqi));
 		if (q)
 			list_add_tail(&q->reset_qnode, qlist);
 	}
@@ -1015,7 +991,7 @@ static void reset_queues_for_apid(struct ap_matrix_mdev *matrix_mdev,
 	struct list_head qlist;
 
 	INIT_LIST_HEAD(&qlist);
-	collect_queues_to_reset(matrix_mdev, apid, &qlist);
+	collect_queues_by_apid(matrix_mdev, apid, &qlist);
 	vfio_ap_mdev_reset_qlist(&qlist);
 }
 
@@ -1031,7 +1007,7 @@ static int reset_queues_for_apids(struct ap_matrix_mdev *matrix_mdev,
 	INIT_LIST_HEAD(&qlist);
 
 	for_each_set_bit_inv(apid, apm_reset, AP_DEVICES)
-		collect_queues_to_reset(matrix_mdev, apid, &qlist);
+		collect_queues_by_apid(matrix_mdev, apid, &qlist);
 
 	return vfio_ap_mdev_reset_qlist(&qlist);
 }
@@ -1702,54 +1678,100 @@ static void ap_matrix_copy(struct ap_matrix *dst, struct ap_matrix *src)
 	bitmap_copy(dst->adm, src->adm, AP_DOMAINS);
 }
 
-static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr,
-			       const char *buf, size_t count)
+static void get_removed_matrixes(struct ap_matrix *m_removed,
+				 struct ap_matrix *m_old,
+				 struct ap_matrix *m_new)
 {
-	struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
-	struct ap_matrix m_new, m_old, m_added, m_removed;
+	bitmap_andnot(m_removed->apm, m_old->apm, m_new->apm, AP_DEVICES);
+	bitmap_andnot(m_removed->aqm, m_old->aqm, m_new->aqm, AP_DOMAINS);
+	bitmap_andnot(m_removed->adm, m_old->adm, m_new->adm, AP_DOMAINS);
+}
+
+static void get_added_matrixes(struct ap_matrix *m_added,
+			       struct ap_matrix *m_old,
+			       struct ap_matrix *m_new)
+{
+	bitmap_andnot(m_added->apm, m_new->apm, m_old->apm, AP_DEVICES);
+	bitmap_andnot(m_added->aqm, m_new->aqm, m_old->aqm, AP_DOMAINS);
+	bitmap_andnot(m_added->adm, m_new->adm, m_old->adm, AP_DOMAINS);
+}
+
+static int validate_new_state(struct ap_matrix_mdev *matrix_mdev)
+{
+	int rc;
+
+	/* Ensure new state is valid, else undo new state */
+	rc = vfio_ap_mdev_validate_masks(matrix_mdev);
+	if (rc)
+		return rc;
+
+	rc = ap_matrix_overflow_check(matrix_mdev);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static void link_new_queues(struct ap_matrix_mdev *matrix_mdev,
+			    struct ap_matrix *m_added)
+{
+	unsigned long apid, apqi;
+
+	for_each_set_bit_inv(apid, m_added->apm, AP_DEVICES)
+		vfio_ap_mdev_link_adapter(matrix_mdev, apid);
+
+	for_each_set_bit_inv(apqi, m_added->aqm, AP_DOMAINS)
+		vfio_ap_mdev_link_domain(matrix_mdev, apqi);
+}
+
+/**
+ * vfio_ap_set_new_guest_config:
+ *
+ * Set a new AP configuration for a guest.
+ *
+ * @matrix_mdev: Object used to maintain the AP configuration for a guest
+ * @m_new:		 Object used to set the new AP configuration
+ *
+ * Returns: zero (0) if the new AP configuration is successfully set; otherwise,
+ *	    returns an error:
+ *
+ *	    ~ EADDRNOTAVAIL One or more APQNs are reserved for host use
+ *	    ~ EADDRINUSE    One or more APQNs are assigned to another mdev
+ *	    ~ ENODEV	    An adapter, domain or control domain in the new
+ *			    AP configuration exceeds the max architected value
+ */
+int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,
+				 struct ap_matrix *m_new)
+{
+	struct ap_matrix m_old, m_old_shadow, m_added, m_removed;
 	DECLARE_BITMAP(apm_filtered, AP_DEVICES);
-	unsigned long newbit;
-	char *newbuf, *rest;
-	int rc = count;
 	bool do_update;
+	int rc;
 
-	newbuf = kstrndup(buf, AP_CONFIG_STRLEN, GFP_KERNEL);
-	if (!newbuf)
-		return -ENOMEM;
-	rest = newbuf;
-
-	mutex_lock(&ap_attr_mutex);
-	get_update_locks_for_mdev(matrix_mdev);
+	lockdep_assert_held(&ap_attr_mutex);
+	assert_has_update_locks_for_mdev(matrix_mdev);
 
-	/* Save old state */
+	/* Save the old state */
 	ap_matrix_copy(&m_old, &matrix_mdev->matrix);
-	if (parse_bitmap(&rest, m_new.apm, AP_DEVICES) ||
-	    parse_bitmap(&rest, m_new.aqm, AP_DOMAINS) ||
-	    parse_bitmap(&rest, m_new.adm, AP_DOMAINS)) {
-		rc = -EINVAL;
-		goto out;
-	}
+	ap_matrix_copy(&m_old_shadow, &matrix_mdev->shadow_apcb);
 
-	bitmap_andnot(m_removed.apm, m_old.apm, m_new.apm, AP_DEVICES);
-	bitmap_andnot(m_removed.aqm, m_old.aqm, m_new.aqm, AP_DOMAINS);
-	bitmap_andnot(m_added.apm, m_new.apm, m_old.apm, AP_DEVICES);
-	bitmap_andnot(m_added.aqm, m_new.aqm, m_old.aqm, AP_DOMAINS);
+	/*
+	 * Get the adapters, domains and control domains added and/or removed
+	 * from the existing configuration
+	 */
+	get_removed_matrixes(&m_removed, &m_old, m_new);
+	get_added_matrixes(&m_added, &m_old, m_new);
 
 	/* Need new bitmaps in matrix_mdev for validation */
-	ap_matrix_copy(&matrix_mdev->matrix, &m_new);
+	ap_matrix_copy(&matrix_mdev->matrix, m_new);
 
 	/* Ensure new state is valid, else undo new state */
-	rc = vfio_ap_mdev_validate_masks(matrix_mdev);
-	if (rc) {
-		ap_matrix_copy(&matrix_mdev->matrix, &m_old);
-		goto out;
-	}
-	rc = ap_matrix_overflow_check(matrix_mdev);
+	rc = validate_new_state(matrix_mdev);
 	if (rc) {
 		ap_matrix_copy(&matrix_mdev->matrix, &m_old);
-		goto out;
+		ap_matrix_copy(&matrix_mdev->shadow_apcb, &m_old_shadow);
+		return rc;
 	}
-	rc = count;
 
 	/* Need old bitmaps in matrix_mdev for unplug/unlink */
 	ap_matrix_copy(&matrix_mdev->matrix, &m_old);
@@ -1759,14 +1781,10 @@ static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr
 	vfio_ap_mdev_hot_unplug_domains(matrix_mdev, m_removed.aqm);
 
 	/* Need new bitmaps in matrix_mdev for linking new adapters/domains */
-	ap_matrix_copy(&matrix_mdev->matrix, &m_new);
-
-	/* Link newly added adapters */
-	for_each_set_bit_inv(newbit, m_added.apm, AP_DEVICES)
-		vfio_ap_mdev_link_adapter(matrix_mdev, newbit);
+	ap_matrix_copy(&matrix_mdev->matrix, m_new);
 
-	for_each_set_bit_inv(newbit, m_added.aqm, AP_DOMAINS)
-		vfio_ap_mdev_link_domain(matrix_mdev, newbit);
+	/* Link queues associated with the newly added adapters and domains */
+	link_new_queues(matrix_mdev, &m_added);
 
 	/* filter resources not bound to vfio-ap */
 	do_update = vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered);
@@ -1777,7 +1795,39 @@ static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr
 		vfio_ap_mdev_update_guest_apcb(matrix_mdev);
 		reset_queues_for_apids(matrix_mdev, apm_filtered);
 	}
-out:
+
+	return 0;
+}
+
+static ssize_t ap_config_store(struct device *dev, struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
+	struct ap_matrix m_new;
+	char *newbuf, *rest;
+	ssize_t rc;
+
+	newbuf = kstrndup(buf, AP_CONFIG_STRLEN, GFP_KERNEL);
+	if (!newbuf)
+		return -ENOMEM;
+	rest = newbuf;
+
+	mutex_lock(&ap_attr_mutex);
+	get_update_locks_for_mdev(matrix_mdev);
+
+	if (parse_bitmap(&rest, m_new.apm, AP_DEVICES) ||
+	    parse_bitmap(&rest, m_new.aqm, AP_DOMAINS) ||
+	    parse_bitmap(&rest, m_new.adm, AP_DOMAINS)) {
+		kfree(newbuf);
+		release_update_locks_for_mdev(matrix_mdev);
+		mutex_unlock(&ap_attr_mutex);
+		return -EINVAL;
+	}
+
+	rc = vfio_ap_set_new_guest_config(matrix_mdev, &m_new);
+	if (!rc)
+		rc = count;
+
 	release_update_locks_for_mdev(matrix_mdev);
 	mutex_unlock(&ap_attr_mutex);
 	kfree(newbuf);
@@ -2052,19 +2102,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);
 }
@@ -2098,6 +2168,20 @@ static void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)
 	release_update_locks_for_mdev(matrix_mdev);
 }
 
+int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix)
+{
+	unsigned long apid, apqi;
+	int num_queues = 0;
+
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+
+	for_each_set_bit_inv(apid, ap_matrix->apm, AP_DEVICES)
+		for_each_set_bit_inv(apqi, ap_matrix->aqm, AP_DOMAINS)
+			num_queues++;
+
+	return num_queues;
+}
+
 static int vfio_ap_mdev_get_device_info(unsigned long arg)
 {
 	unsigned long minsz;
@@ -2275,6 +2359,10 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,
 		break;
 	case VFIO_DEVICE_RESET:
 		ret = vfio_ap_mdev_reset_queues(matrix_mdev);
+		if (ret == 0) {
+			/* Reset migration state per VFIO migration spec */
+			vfio_ap_reset_migration_state(matrix_mdev);
+		}
 		break;
 	case VFIO_DEVICE_GET_IRQ_INFO:
 		ret = vfio_ap_get_irq_info(arg);
@@ -2360,6 +2448,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 9bff666b0b35d3..150dfce8a674b2 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;
 };
 
 /**
@@ -152,6 +157,64 @@ struct vfio_ap_queue {
 	struct work_struct reset_work;
 };
 
+/**
+ * get_update_locks_for_mdev: Acquire the locks required to dynamically update a
+ *			      KVM guest's APCB in the proper order.
+ *
+ * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
+ *		 configuration data to use to update a KVM guest's APCB.
+ *
+ * The proper locking order is:
+ * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
+ *			       guest's APCB.
+ * 2. matrix_mdev->kvm->lock:  required to update a guest's APCB
+ * 3. matrix_dev->mdevs_lock:  required to access data stored in a matrix_mdev
+ *
+ * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
+ *	 lock will not be taken.
+ */
+static inline void get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
+{
+	mutex_lock(&matrix_dev->guests_lock);
+	if (matrix_mdev && matrix_mdev->kvm)
+		mutex_lock(&matrix_mdev->kvm->lock);
+	mutex_lock(&matrix_dev->mdevs_lock);
+}
+
+/**
+ * release_update_locks_for_mdev: Release the locks used to dynamically update a
+ *				  KVM guest's APCB in the proper order.
+ *
+ * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP
+ *		 configuration data to use to update a KVM guest's APCB.
+ *
+ * The proper unlocking order is:
+ * 1. matrix_dev->mdevs_lock
+ * 2. matrix_mdev->kvm->lock
+ * 3. matrix_dev->guests_lock
+ *
+ * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM
+ *	 lock will not be released.
+ */
+static inline void release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
+{
+	mutex_unlock(&matrix_dev->mdevs_lock);
+	if (matrix_mdev && matrix_mdev->kvm)
+		mutex_unlock(&matrix_mdev->kvm->lock);
+	mutex_unlock(&matrix_dev->guests_lock);
+}
+
+static inline void
+assert_has_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
+{
+	lockdep_assert_held(&matrix_dev->guests_lock);
+	if (matrix_mdev && matrix_mdev->kvm)
+		lockdep_assert_held(&matrix_mdev->kvm->lock);
+	lockdep_assert_held(&matrix_dev->mdevs_lock);
+}
+
+int vfio_ap_mdev_get_num_queues(struct ap_matrix *ap_matrix);
+
 int vfio_ap_mdev_register(void);
 void vfio_ap_mdev_unregister(void);
 
@@ -165,4 +228,14 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,
 void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
 			      struct ap_config_info *old_config_info);
 
+void vfio_ap_matrix_init(struct ap_config_info *info, struct ap_matrix *matrix);
+
+void vfio_ap_init_migration_capabilities(struct ap_matrix_mdev *matrix_mdev);
+int vfio_ap_init_migration_data(struct ap_matrix_mdev *matrix_mdev);
+void vfio_ap_release_migration_data(struct ap_matrix_mdev *matrix_mdev);
+void vfio_ap_reset_migration_state(struct ap_matrix_mdev *matrix_mdev);
+
+int vfio_ap_set_new_guest_config(struct ap_matrix_mdev *matrix_mdev,
+				 struct ap_matrix *m_new);
+
 #endif /* _VFIO_AP_PRIVATE_H_ */

3/2 2026/07/26 08:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 37697
							output: 23
							thoughts: 361
						

Thoughts:
**VFIO AP Migration on s390: Initial Assessment**

Okay, so this patch introduces live guest migration support specifically for the `vfio_ap` mediated device on the s390 architecture. It looks like it's adding some new code, namely `vfio_ap_migration.c`, and modifying existing files within the `vfio_ap` driver. This is interesting – it allows for migrating the AP configuration metadata of a guest, which is, of course, very specific to s390 and VFIO AP hardware, or an appropriate emulation.

My immediate question is: Is this even relevant in common environments? Will this patch be useful outside of specialized setups? Thinking about standard QEMU or perhaps GCE (Google Compute Engine), do they provide the necessary infrastructure? I know s390 is supported by QEMU, that's clear. But the VFIO AP part... that requires specific hardware, AP crypto cards, or an emulation of that hardware, which is the crux of the issue.

A further question arose, can we even test this with syzkaller? Syzkaller doesn't typically target s390x by default in most generic setups, though I do recall it *does* have an s390x port. The real issue is whether standard QEMU actually *emulates* AP crypto cards. I need to figure out if there's any software emulation available for AP crypto cards in QEMU, or if it's purely pass-through.

I need to circle back to the patch description: it states the `vfio_ap` mediated device isn't used for direct device memory access, unlike other VFIO devices. Instead, it manages AP configuration metadata. That changes things. Since it's a *mediated* device driver, does that mean it can be instantiated without requiring physical AP hardware? If so, this is very important. I should dig deeper and start by looking at `drivers/s390/crypto/vfio_ap_drv.c` and `vfio_ap_ops.c`. This is where I'll probably find my answer.



4/2 2026/07/26 08:49 tool grepper
0m Args:
{
  "Expression": "vfio_ap_mdev_probe"
}

Results:
{
  "Output": "drivers/s390/crypto/vfio_ap_drv.c=60=static struct ap_driver vfio_ap_drv = {\ndrivers/s390/crypto/vfio_ap_drv.c:61:\t.probe = vfio_ap_mdev_probe_queue,\ndrivers/s390/crypto/vfio_ap_drv.c-62-\t.remove = vfio_ap_mdev_remove_queue,\n--\ndrivers/s390/crypto/vfio_ap_ops.c=744=static void vfio_ap_mdev_release_dev(struct vfio_device *vdev)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-753-\ndrivers/s390/crypto/vfio_ap_ops.c:754:static int vfio_ap_mdev_probe(struct mdev_device *mdev)\ndrivers/s390/crypto/vfio_ap_ops.c-755-{\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2463=static struct mdev_driver vfio_ap_matrix_driver = {\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2471-\t},\ndrivers/s390/crypto/vfio_ap_ops.c:2472:\t.probe = vfio_ap_mdev_probe,\ndrivers/s390/crypto/vfio_ap_ops.c-2473-\t.remove = vfio_ap_mdev_remove,\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2499=void vfio_ap_mdev_unregister(void)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2504-\ndrivers/s390/crypto/vfio_ap_ops.c:2505:int vfio_ap_mdev_probe_queue(struct ap_device *apdev)\ndrivers/s390/crypto/vfio_ap_ops.c-2506-{\n--\ndrivers/s390/crypto/vfio_ap_private.h=219=void vfio_ap_mdev_unregister(void);\ndrivers/s390/crypto/vfio_ap_private.h-220-\ndrivers/s390/crypto/vfio_ap_private.h:221:int vfio_ap_mdev_probe_queue(struct ap_device *queue);\ndrivers/s390/crypto/vfio_ap_private.h-222-void vfio_ap_mdev_remove_queue(struct ap_device *queue);\n"
}

5/2 2026/07/26 08:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 39222
							output: 22
							thoughts: 85
						

Thoughts:
**Investigating the `vfio_ap` Driver and Mediated Devices**

Okay, so I'm looking at the `vfio_ap` driver – it's a mediated device driver, right? Registers itself with the `mdev` framework, which makes perfect sense. I usually interact with `mdev` through sysfs file writes to create those things, no surprises there.

But here's a thought: Can I potentially spin up a `vfio_ap` mdev even if there's no actual AP hardware present? I need to understand that. I should probably dive into `vfio_ap_mdev_probe` and `vfio_ap_matrix_driver` to see how those pieces fit together. They'll likely hold the key to how the device is initialized and if physical hardware presence is strictly enforced, or if some form of virtual setup is allowed or designed. Let's see what the code reveals.



6/2 2026/07/26 08:49 tool grepper
0m Args:
{
  "Expression": "vfio_ap_matrix_driver"
}

Results:
{
  "Output": "drivers/s390/crypto/vfio_ap_ops.c=2449=static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2462-\ndrivers/s390/crypto/vfio_ap_ops.c:2463:static struct mdev_driver vfio_ap_matrix_driver = {\ndrivers/s390/crypto/vfio_ap_ops.c-2464-\t.device_api = VFIO_DEVICE_API_AP_STRING,\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2476=int vfio_ap_mdev_register(void)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2479-\ndrivers/s390/crypto/vfio_ap_ops.c:2480:\tret = mdev_register_driver(\u0026vfio_ap_matrix_driver);\ndrivers/s390/crypto/vfio_ap_ops.c-2481-\tif (ret)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2487-\tret = mdev_register_parent(\u0026matrix_dev-\u003eparent, \u0026matrix_dev-\u003edevice,\ndrivers/s390/crypto/vfio_ap_ops.c:2488:\t\t\t\t   \u0026vfio_ap_matrix_driver,\ndrivers/s390/crypto/vfio_ap_ops.c-2489-\t\t\t\t   \u0026matrix_dev-\u003emdev_types, 1);\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2494-err_driver:\ndrivers/s390/crypto/vfio_ap_ops.c:2495:\tmdev_unregister_driver(\u0026vfio_ap_matrix_driver);\ndrivers/s390/crypto/vfio_ap_ops.c-2496-\treturn ret;\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2499=void vfio_ap_mdev_unregister(void)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2501-\tmdev_unregister_parent(\u0026matrix_dev-\u003eparent);\ndrivers/s390/crypto/vfio_ap_ops.c:2502:\tmdev_unregister_driver(\u0026vfio_ap_matrix_driver);\ndrivers/s390/crypto/vfio_ap_ops.c-2503-}\n"
}

7/2 2026/07/26 08:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 40196
							output: 19
							thoughts: 253
						

Thoughts:
**My Assessment of the WorthFuzzing Evaluation**

Okay, so the `vfio_ap` driver depends on `CONFIG_VFIO_AP`, that much is clear. My immediate thought is, "Is this AP bus even accessible in a standard QEMU setup?" I'd normally dive into the kernel source, probably starting with `drivers/s390/crypto/ap_bus.c` or something analogous. But wait a minute... the instructions are telling me to return `WorthFuzzing=false` if the changes are unreachable in standard virtualized environments. That means GCE or QEMU using *software* emulated hardware. Hardware which is not vendor specific. This means no physical PCIe hardware cards like specific switches, SmartNICs, or GPUs.

So, the question becomes: does QEMU offer software emulation for s390 AP crypto hardware? I know QEMU *has* s390x crypto device support, but typically it leans towards pass-through via vfio-ap. I need to figure out if QEMU can emulate the AP bus purely in software, or if the kernel can create AP devices without any physical (or emulated-physical) dependency. That's the key to this evaluation.



8/2 2026/07/26 08:49 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "ap_device_register"
}

Results:
{
  "error": "no matches"
}

9/2 2026/07/26 08:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 40747
							output: 17
							thoughts: 19
						

Thoughts:
Here's my thought process, as it stands:

**Navigating the AP Bus**

Okay, I need to investigate the AP bus interactions. My initial instinct is to dive directly into the relevant source files. Given what I'm looking at, the most likely candidates for relevant code are `ap_bus.c` and `ap_bus.h`. I'll start by checking those files. Let's start with those.



10/2 2026/07/26 08:49 tool grepper
0m Args:
{
  "Expression": "ap_bus"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 2312 lines.\nUse more precise expression if possible.\n\nDocumentation/arch/s390/vfio-ap.rst=211=reserved::\n--\nDocumentation/arch/s390/vfio-ap.rst-231-  |                  |                      |                  |\nDocumentation/arch/s390/vfio-ap.rst:232:  |      ap_bus      +--------------------- \u003e  vfio_ap driver  |\nDocumentation/arch/s390/vfio-ap.rst-233-  |                  |       8 probe        |                  |\n--\narch/arm/mach-omap2/omap_device.c-17- * In the medium- to long-term, this code should be implemented as a\narch/arm/mach-omap2/omap_device.c:18: * proper omap_bus/omap_device in Linux, no more platform_data func\narch/arm/mach-omap2/omap_device.c-19- * pointers\n--\narch/arm/mach-omap2/omap_device.h-13- * This type of functionality should be implemented as a proper\narch/arm/mach-omap2/omap_device.h:14: * omap_bus/omap_device in Linux.\narch/arm/mach-omap2/omap_device.h-15- *\n--\narch/csky/include/asm/traps.h=46=asmlinkage void do_trap_zdiv(struct pt_regs *regs);\narch/csky/include/asm/traps.h:47:asmlinkage void do_trap_buserr(struct pt_regs *regs);\narch/csky/include/asm/traps.h-48-asmlinkage void do_trap_misaligned(struct pt_regs *regs);\n--\narch/csky/kernel/traps.c=153=DO_ERROR_INFO(do_trap_zdiv,\narch/csky/kernel/traps.c-154-\tSIGFPE, FPE_INTDIV, \"error zero div exception\");\narch/csky/kernel/traps.c:155:DO_ERROR_INFO(do_trap_buserr,\narch/csky/kernel/traps.c-156-\tSIGSEGV, ILL_ILLADR, \"error bus error exception\");\n--\narch/csky/kernel/traps.c=231=asmlinkage void trap_c(struct pt_regs *regs)\n--\narch/csky/kernel/traps.c-247-\tcase VEC_ACCESS:\narch/csky/kernel/traps.c:248:\t\tdo_trap_buserr(regs);\narch/csky/kernel/traps.c-249-\t\tbreak;\n--\narch/loongarch/pci/pci.c=111=static void loongson_gpu_fixup_dma_hang(struct pci_dev *pdev, bool on)\n--\narch/loongarch/pci/pci.c-116-\narch/loongarch/pci/pci.c:117:\tbase = pdev-\u003ebus-\u003eops-\u003emap_bus(pdev-\u003ebus, pdev-\u003edevfn + 1, 0);\narch/loongarch/pci/pci.c-118-\tdevice = readw(base + PCI_DEVICE_ID);\n--\narch/powerpc/platforms/powermac/pci.c=88=static void __init fixup_bus_range(struct device_node *bridge)\n--\narch/powerpc/platforms/powermac/pci.c-134-\narch/powerpc/platforms/powermac/pci.c:135:static void __iomem *macrisc_cfg_map_bus(struct pci_bus *bus,\narch/powerpc/platforms/powermac/pci.c-136-\t\t\t\t\t unsigned int dev_fn,\n--\narch/powerpc/platforms/powermac/pci.c=162=static struct pci_ops macrisc_pci_ops =\narch/powerpc/platforms/powermac/pci.c-163-{\narch/powerpc/platforms/powermac/pci.c:164:\t.map_bus = macrisc_cfg_map_bus,\narch/powerpc/platforms/powermac/pci.c-165-\t.read = pci_generic_config_read,\n--\narch/powerpc/platforms/powermac/pci.c-172- */\narch/powerpc/platforms/powermac/pci.c:173:static void __iomem *chaos_map_bus(struct pci_bus *bus, unsigned int devfn,\narch/powerpc/platforms/powermac/pci.c-174-\t\t\t\t   int offset)\n--\narch/powerpc/platforms/powermac/pci.c-193-\narch/powerpc/platforms/powermac/pci.c:194:\treturn macrisc_cfg_map_bus(bus, devfn, offset);\narch/powerpc/platforms/powermac/pci.c-195-}\n--\narch/powerpc/platforms/powermac/pci.c=197=static struct pci_ops chaos_pci_ops =\narch/powerpc/platforms/powermac/pci.c-198-{\narch/powerpc/platforms/powermac/pci.c:199:\t.map_bus = chaos_map_bus,\narch/powerpc/platforms/powermac/pci.c-200-\t.read = pci_generic_config_read,\n--\narch/powerpc/platforms/powermac/pci.c=377=static struct pci_ops u3_ht_pci_ops =\n--\narch/powerpc/platforms/powermac/pci.c-395-\narch/powerpc/platforms/powermac/pci.c:396:static void __iomem *u4_pcie_cfg_map_bus(struct pci_bus *bus,\narch/powerpc/platforms/powermac/pci.c-397-\t\t\t\t\t unsigned int dev_fn,\n--\narch/powerpc/platforms/powermac/pci.c=424=static struct pci_ops u4_pcie_pci_ops =\narch/powerpc/platforms/powermac/pci.c-425-{\narch/powerpc/platforms/powermac/pci.c:426:\t.map_bus = u4_pcie_cfg_map_bus,\narch/powerpc/platforms/powermac/pci.c-427-\t.read = pci_generic_config_read,\n--\narch/powerpc/sysdev/fsl_pci.c=757=static struct pci_ops mpc83xx_pcie_ops = {\narch/powerpc/sysdev/fsl_pci.c:758:\t.map_bus = mpc83xx_pcie_remap_cfg,\narch/powerpc/sysdev/fsl_pci.c-759-\t.read = pci_generic_config_read,\n--\narch/um/drivers/virt-pci.c=175=static const struct logic_iomem_ops um_pci_device_bar_ops = {\n--\narch/um/drivers/virt-pci.c-182-\narch/um/drivers/virt-pci.c:183:static void __iomem *um_pci_map_bus(struct pci_bus *bus, unsigned int devfn,\narch/um/drivers/virt-pci.c-184-\t\t\t\t    int where)\n--\narch/um/drivers/virt-pci.c=206=static struct pci_ops um_pci_ops = {\narch/um/drivers/virt-pci.c:207:\t.map_bus = um_pci_map_bus,\narch/um/drivers/virt-pci.c-208-\t.read = pci_generic_config_read,\n--\nblock/blk-mq-dma.c=74=static inline bool blk_can_dma_map_iova(struct request *req,\n--\nblock/blk-mq-dma.c-79-\nblock/blk-mq-dma.c:80:static bool blk_dma_map_bus(struct blk_dma_iter *iter, struct phys_vec *vec)\nblock/blk-mq-dma.c-81-{\n--\nblock/blk-mq-dma.c=164=static bool blk_dma_map_iter_start(struct request *req, struct device *dma_dev,\n--\nblock/blk-mq-dma.c-183-\tcase PCI_P2PDMA_MAP_BUS_ADDR:\nblock/blk-mq-dma.c:184:\t\treturn blk_dma_map_bus(iter, \u0026vec);\nblock/blk-mq-dma.c-185-\tcase PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:\n--\nblock/blk-mq-dma.c=252=bool blk_rq_dma_map_iter_next(struct request *req, struct device *dma_dev,\n--\nblock/blk-mq-dma.c-260-\tif (iter-\u003ep2pdma.map == PCI_P2PDMA_MAP_BUS_ADDR)\nblock/blk-mq-dma.c:261:\t\treturn blk_dma_map_bus(iter, \u0026vec);\nblock/blk-mq-dma.c-262-\treturn blk_dma_map_direct(req, dma_dev, iter, \u0026vec);\n--\nblock/blk-mq-dma.c=378=bool blk_rq_integrity_dma_map_iter_next(struct request *req,\n--\nblock/blk-mq-dma.c-386-\tif (iter-\u003ep2pdma.map == PCI_P2PDMA_MAP_BUS_ADDR)\nblock/blk-mq-dma.c:387:\t\treturn blk_dma_map_bus(iter, \u0026vec);\nblock/blk-mq-dma.c-388-\treturn blk_dma_map_direct(req, dma_dev, iter, \u0026vec);\n--\ndrivers/base/regmap/internal.h=50=struct regmap {\n--\ndrivers/base/regmap/internal.h-71-\tstruct regmap_format format;  /* Buffer format */\ndrivers/base/regmap/internal.h:72:\tconst struct regmap_bus *bus;\ndrivers/base/regmap/internal.h-73-\tvoid *bus_context;\n--\ndrivers/base/regmap/internal.h=292=enum regmap_endian regmap_get_val_endian(struct device *dev,\ndrivers/base/regmap/internal.h:293:\t\t\t\t\t const struct regmap_bus *bus,\ndrivers/base/regmap/internal.h-294-\t\t\t\t\t const struct regmap_config *config);\n--\ndrivers/base/regmap/regmap-ac97.c=54=static int regmap_ac97_reg_write(void *context, unsigned int reg,\n--\ndrivers/base/regmap/regmap-ac97.c-63-\ndrivers/base/regmap/regmap-ac97.c:64:static const struct regmap_bus ac97_regmap_bus = {\ndrivers/base/regmap/regmap-ac97.c-65-\t.reg_write = regmap_ac97_reg_write,\n--\ndrivers/base/regmap/regmap-ac97.c=69=struct regmap *__regmap_init_ac97(struct snd_ac97 *ac97,\n--\ndrivers/base/regmap/regmap-ac97.c-73-{\ndrivers/base/regmap/regmap-ac97.c:74:\treturn __regmap_init(\u0026ac97-\u003edev, \u0026ac97_regmap_bus, ac97, config,\ndrivers/base/regmap/regmap-ac97.c-75-\t\t\t     lock_key, lock_name);\n--\ndrivers/base/regmap/regmap-ac97.c=79=struct regmap *__devm_regmap_init_ac97(struct snd_ac97 *ac97,\n--\ndrivers/base/regmap/regmap-ac97.c-83-{\ndrivers/base/regmap/regmap-ac97.c:84:\treturn __devm_regmap_init(\u0026ac97-\u003edev, \u0026ac97_regmap_bus, ac97, config,\ndrivers/base/regmap/regmap-ac97.c-85-\t\t\t\t  lock_key, lock_name);\n--\ndrivers/base/regmap/regmap-fsi.c=28=static int regmap_fsi32_reg_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-fsi.c-34-\ndrivers/base/regmap/regmap-fsi.c:35:static const struct regmap_bus regmap_fsi32 = {\ndrivers/base/regmap/regmap-fsi.c-36-\t.reg_write = regmap_fsi32_reg_write,\n--\ndrivers/base/regmap/regmap-fsi.c=53=static int regmap_fsi32le_reg_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-fsi.c-59-\ndrivers/base/regmap/regmap-fsi.c:60:static const struct regmap_bus regmap_fsi32le = {\ndrivers/base/regmap/regmap-fsi.c-61-\t.reg_write = regmap_fsi32le_reg_write,\n--\ndrivers/base/regmap/regmap-fsi.c=78=static int regmap_fsi16_reg_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-fsi.c-88-\ndrivers/base/regmap/regmap-fsi.c:89:static const struct regmap_bus regmap_fsi16 = {\ndrivers/base/regmap/regmap-fsi.c-90-\t.reg_write = regmap_fsi16_reg_write,\n--\ndrivers/base/regmap/regmap-fsi.c=107=static int regmap_fsi16le_reg_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-fsi.c-117-\ndrivers/base/regmap/regmap-fsi.c:118:static const struct regmap_bus regmap_fsi16le = {\ndrivers/base/regmap/regmap-fsi.c-119-\t.reg_write = regmap_fsi16le_reg_write,\n--\ndrivers/base/regmap/regmap-fsi.c=136=static int regmap_fsi8_reg_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-fsi.c-146-\ndrivers/base/regmap/regmap-fsi.c:147:static const struct regmap_bus regmap_fsi8 = {\ndrivers/base/regmap/regmap-fsi.c-148-\t.reg_write = regmap_fsi8_reg_write,\n--\ndrivers/base/regmap/regmap-fsi.c-151-\ndrivers/base/regmap/regmap-fsi.c:152:static const struct regmap_bus *regmap_get_fsi_bus(struct fsi_device *fsi_dev,\ndrivers/base/regmap/regmap-fsi.c-153-\t\t\t\t\t\t   const struct regmap_config *config)\ndrivers/base/regmap/regmap-fsi.c-154-{\ndrivers/base/regmap/regmap-fsi.c:155:\tconst struct regmap_bus *bus = NULL;\ndrivers/base/regmap/regmap-fsi.c-156-\n--\ndrivers/base/regmap/regmap-fsi.c=206=struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config,\n--\ndrivers/base/regmap/regmap-fsi.c-208-{\ndrivers/base/regmap/regmap-fsi.c:209:\tconst struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);\ndrivers/base/regmap/regmap-fsi.c-210-\n--\ndrivers/base/regmap/regmap-fsi.c=218=struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev,\n--\ndrivers/base/regmap/regmap-fsi.c-221-{\ndrivers/base/regmap/regmap-fsi.c:222:\tconst struct regmap_bus *bus = regmap_get_fsi_bus(fsi_dev, config);\ndrivers/base/regmap/regmap-fsi.c-223-\n--\ndrivers/base/regmap/regmap-i2c.c=34=static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,\n--\ndrivers/base/regmap/regmap-i2c.c-45-\ndrivers/base/regmap/regmap-i2c.c:46:static const struct regmap_bus regmap_smbus_byte = {\ndrivers/base/regmap/regmap-i2c.c-47-\t.reg_write = regmap_smbus_byte_reg_write,\n--\ndrivers/base/regmap/regmap-i2c.c=70=static int regmap_smbus_word_reg_write(void *context, unsigned int reg,\n--\ndrivers/base/regmap/regmap-i2c.c-81-\ndrivers/base/regmap/regmap-i2c.c:82:static const struct regmap_bus regmap_smbus_word = {\ndrivers/base/regmap/regmap-i2c.c-83-\t.reg_write = regmap_smbus_word_reg_write,\n--\ndrivers/base/regmap/regmap-i2c.c=106=static int regmap_smbus_word_write_swapped(void *context, unsigned int reg,\n--\ndrivers/base/regmap/regmap-i2c.c-117-\ndrivers/base/regmap/regmap-i2c.c:118:static const struct regmap_bus regmap_smbus_word_swapped = {\ndrivers/base/regmap/regmap-i2c.c-119-\t.reg_write = regmap_smbus_word_write_swapped,\n--\ndrivers/base/regmap/regmap-i2c.c=172=static int regmap_i2c_read(void *context,\n--\ndrivers/base/regmap/regmap-i2c.c-199-\ndrivers/base/regmap/regmap-i2c.c:200:static const struct regmap_bus regmap_i2c = {\ndrivers/base/regmap/regmap-i2c.c-201-\t.write = regmap_i2c_write,\n--\ndrivers/base/regmap/regmap-i2c.c=222=static int regmap_i2c_smbus_i2c_read(void *context, const void *reg,\n--\ndrivers/base/regmap/regmap-i2c.c-241-\ndrivers/base/regmap/regmap-i2c.c:242:static const struct regmap_bus regmap_i2c_smbus_i2c_block = {\ndrivers/base/regmap/regmap-i2c.c-243-\t.write = regmap_i2c_smbus_i2c_write,\n--\ndrivers/base/regmap/regmap-i2c.c=263=static int regmap_i2c_smbus_i2c_read_reg16(void *context, const void *reg,\n--\ndrivers/base/regmap/regmap-i2c.c-298-\ndrivers/base/regmap/regmap-i2c.c:299:static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {\ndrivers/base/regmap/regmap-i2c.c-300-\t.write = regmap_i2c_smbus_i2c_write_reg16,\n--\ndrivers/base/regmap/regmap-i2c.c=320=static int regmap_smbus_word_write_reg16(void *context, const void *data,\n--\ndrivers/base/regmap/regmap-i2c.c-342-\ndrivers/base/regmap/regmap-i2c.c:343:static const struct regmap_bus regmap_smbus_byte_word_reg16 = {\ndrivers/base/regmap/regmap-i2c.c-344-\t.write = regmap_smbus_word_write_reg16,\n--\ndrivers/base/regmap/regmap-i2c.c-349-\ndrivers/base/regmap/regmap-i2c.c:350:static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,\ndrivers/base/regmap/regmap-i2c.c-351-\t\t\t\t\tconst struct regmap_config *config)\n--\ndrivers/base/regmap/regmap-i2c.c-353-\tconst struct i2c_adapter_quirks *quirks;\ndrivers/base/regmap/regmap-i2c.c:354:\tconst struct regmap_bus *bus = NULL;\ndrivers/base/regmap/regmap-i2c.c:355:\tstruct regmap_bus *ret_bus;\ndrivers/base/regmap/regmap-i2c.c-356-\tu16 max_read = 0, max_write = 0;\n--\ndrivers/base/regmap/regmap-i2c.c=419=struct regmap *__regmap_init_i2c(struct i2c_client *i2c,\n--\ndrivers/base/regmap/regmap-i2c.c-423-{\ndrivers/base/regmap/regmap-i2c.c:424:\tconst struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);\ndrivers/base/regmap/regmap-i2c.c-425-\n--\ndrivers/base/regmap/regmap-i2c.c=434=struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,\n--\ndrivers/base/regmap/regmap-i2c.c-438-{\ndrivers/base/regmap/regmap-i2c.c:439:\tconst struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);\ndrivers/base/regmap/regmap-i2c.c-440-\n--\ndrivers/base/regmap/regmap-i3c.c=25=static int regmap_i3c_read(void *context,\n--\ndrivers/base/regmap/regmap-i3c.c-43-\ndrivers/base/regmap/regmap-i3c.c:44:static const struct regmap_bus regmap_i3c = {\ndrivers/base/regmap/regmap-i3c.c-45-\t.write = regmap_i3c_write,\n--\ndrivers/base/regmap/regmap-mdio.c=30=static int regmap_mdio_c22_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-mdio.c-39-\ndrivers/base/regmap/regmap-mdio.c:40:static const struct regmap_bus regmap_mdio_c22_bus = {\ndrivers/base/regmap/regmap-mdio.c-41-\t.reg_write = regmap_mdio_c22_write,\n--\ndrivers/base/regmap/regmap-mdio.c=66=static int regmap_mdio_c45_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-mdio.c-79-\ndrivers/base/regmap/regmap-mdio.c:80:static const struct regmap_bus regmap_mdio_c45_bus = {\ndrivers/base/regmap/regmap-mdio.c-81-\t.reg_write = regmap_mdio_c45_write,\n--\ndrivers/base/regmap/regmap-mdio.c=85=struct regmap *__regmap_init_mdio(struct mdio_device *mdio_dev,\n--\ndrivers/base/regmap/regmap-mdio.c-88-{\ndrivers/base/regmap/regmap-mdio.c:89:\tconst struct regmap_bus *bus;\ndrivers/base/regmap/regmap-mdio.c-90-\n--\ndrivers/base/regmap/regmap-mdio.c=102=struct regmap *__devm_regmap_init_mdio(struct mdio_device *mdio_dev,\n--\ndrivers/base/regmap/regmap-mdio.c-105-{\ndrivers/base/regmap/regmap-mdio.c:106:\tconst struct regmap_bus *bus;\ndrivers/base/regmap/regmap-mdio.c-107-\n--\ndrivers/base/regmap/regmap-mmio.c=385=static void regmap_mmio_free_context(void *context)\n--\ndrivers/base/regmap/regmap-mmio.c-396-\ndrivers/base/regmap/regmap-mmio.c:397:static const struct regmap_bus regmap_mmio = {\ndrivers/base/regmap/regmap-mmio.c-398-\t.fast_io = true,\n--\ndrivers/base/regmap/regmap-ram.c=39=static void regmap_ram_free_context(void *context)\n--\ndrivers/base/regmap/regmap-ram.c-48-\ndrivers/base/regmap/regmap-ram.c:49:static const struct regmap_bus regmap_ram = {\ndrivers/base/regmap/regmap-ram.c-50-\t.fast_io = true,\n--\ndrivers/base/regmap/regmap-raw-ram.c=92=static void regmap_raw_ram_free_context(void *context)\n--\ndrivers/base/regmap/regmap-raw-ram.c-101-\ndrivers/base/regmap/regmap-raw-ram.c:102:static const struct regmap_bus regmap_raw_ram = {\ndrivers/base/regmap/regmap-raw-ram.c-103-\t.fast_io = true,\n--\ndrivers/base/regmap/regmap-sccb.c=75=static int regmap_sccb_write(void *context, unsigned int reg, unsigned int val)\n--\ndrivers/base/regmap/regmap-sccb.c-82-\ndrivers/base/regmap/regmap-sccb.c:83:static const struct regmap_bus regmap_sccb_bus = {\ndrivers/base/regmap/regmap-sccb.c-84-\t.reg_write = regmap_sccb_write,\n--\ndrivers/base/regmap/regmap-sccb.c-87-\ndrivers/base/regmap/regmap-sccb.c:88:static const struct regmap_bus *regmap_get_sccb_bus(struct i2c_client *i2c,\ndrivers/base/regmap/regmap-sccb.c-89-\t\t\t\t\tconst struct regmap_config *config)\n--\ndrivers/base/regmap/regmap-sccb.c=98=struct regmap *__regmap_init_sccb(struct i2c_client *i2c,\n--\ndrivers/base/regmap/regmap-sccb.c-102-{\ndrivers/base/regmap/regmap-sccb.c:103:\tconst struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config);\ndrivers/base/regmap/regmap-sccb.c-104-\n--\ndrivers/base/regmap/regmap-sccb.c=113=struct regmap *__devm_regmap_init_sccb(struct i2c_client *i2c,\n--\ndrivers/base/regmap/regmap-sccb.c-117-{\ndrivers/base/regmap/regmap-sccb.c:118:\tconst struct regmap_bus *bus = regmap_get_sccb_bus(i2c, config);\ndrivers/base/regmap/regmap-sccb.c-119-\n--\ndrivers/base/regmap/regmap-sdw-mbq.c=153=static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)\n--\ndrivers/base/regmap/regmap-sdw-mbq.c-186-\ndrivers/base/regmap/regmap-sdw-mbq.c:187:static const struct regmap_bus regmap_sdw_mbq = {\ndrivers/base/regmap/regmap-sdw-mbq.c-188-\t.reg_read = regmap_sdw_mbq_read,\n--\ndrivers/base/regmap/regmap-sdw.c=34=static int regmap_sdw_read(void *context,\n--\ndrivers/base/regmap/regmap-sdw.c-44-\ndrivers/base/regmap/regmap-sdw.c:45:static const struct regmap_bus regmap_sdw = {\ndrivers/base/regmap/regmap-sdw.c-46-\t.write = regmap_sdw_write,\n--\ndrivers/base/regmap/regmap-slimbus.c=17=static int regmap_slimbus_read(void *context, const void *reg, size_t reg_size,\n--\ndrivers/base/regmap/regmap-slimbus.c-24-\ndrivers/base/regmap/regmap-slimbus.c:25:static const struct regmap_bus regmap_slimbus_bus = {\ndrivers/base/regmap/regmap-slimbus.c-26-\t.write = regmap_slimbus_write,\n--\ndrivers/base/regmap/regmap-slimbus.c-31-\ndrivers/base/regmap/regmap-slimbus.c:32:static const struct regmap_bus *regmap_get_slimbus(struct slim_device *slim,\ndrivers/base/regmap/regmap-slimbus.c-33-\t\t\t\t\tconst struct regmap_config *config)\n--\ndrivers/base/regmap/regmap-slimbus.c=41=struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,\n--\ndrivers/base/regmap/regmap-slimbus.c-45-{\ndrivers/base/regmap/regmap-slimbus.c:46:\tconst struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);\ndrivers/base/regmap/regmap-slimbus.c-47-\n--\ndrivers/base/regmap/regmap-slimbus.c=55=struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,\n--\ndrivers/base/regmap/regmap-slimbus.c-59-{\ndrivers/base/regmap/regmap-slimbus.c:60:\tconst struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);\ndrivers/base/regmap/regmap-slimbus.c-61-\n--\ndrivers/base/regmap/regmap-spi-avmm.c=651=static void spi_avmm_bridge_ctx_free(void *context)\n--\ndrivers/base/regmap/regmap-spi-avmm.c-655-\ndrivers/base/regmap/regmap-spi-avmm.c:656:static const struct regmap_bus regmap_spi_avmm_bus = {\ndrivers/base/regmap/regmap-spi-avmm.c-657-\t.write = regmap_spi_avmm_write,\n--\ndrivers/base/regmap/regmap-spi.c=91=static int regmap_spi_read(void *context,\n--\ndrivers/base/regmap/regmap-spi.c-100-\ndrivers/base/regmap/regmap-spi.c:101:static const struct regmap_bus regmap_spi = {\ndrivers/base/regmap/regmap-spi.c-102-\t.write = regmap_spi_write,\n--\ndrivers/base/regmap/regmap-spi.c-111-\ndrivers/base/regmap/regmap-spi.c:112:static const struct regmap_bus *regmap_get_spi_bus(struct spi_device *spi,\ndrivers/base/regmap/regmap-spi.c-113-\t\t\t\t\t\t   const struct regmap_config *config)\n--\ndrivers/base/regmap/regmap-spi.c-116-\tsize_t max_msg_size, reg_reserve_size;\ndrivers/base/regmap/regmap-spi.c:117:\tstruct regmap_bus *bus;\ndrivers/base/regmap/regmap-spi.c-118-\n--\ndrivers/base/regmap/regmap-spi.c=139=struct regmap *__regmap_init_spi(struct spi_device *spi,\n--\ndrivers/base/regmap/regmap-spi.c-143-{\ndrivers/base/regmap/regmap-spi.c:144:\tconst struct regmap_bus *bus = regmap_get_spi_bus(spi, config);\ndrivers/base/regmap/regmap-spi.c-145-\n--\ndrivers/base/regmap/regmap-spi.c=153=struct regmap *__devm_regmap_init_spi(struct spi_device *spi,\n--\ndrivers/base/regmap/regmap-spi.c-157-{\ndrivers/base/regmap/regmap-spi.c:158:\tconst struct regmap_bus *bus = regmap_get_spi_bus(spi, config);\ndrivers/base/regmap/regmap-spi.c-159-\n--\ndrivers/base/regmap/regmap-spmi.c=69=static int regmap_spmi_base_write(void *context, const void *data,\n--\ndrivers/base/regmap/regmap-spmi.c-76-\ndrivers/base/regmap/regmap-spmi.c:77:static const struct regmap_bus regmap_spmi_base = {\ndrivers/base/regmap/regmap-spmi.c-78-\t.read\t\t\t\t= regmap_spmi_base_read,\n--\ndrivers/base/regmap/regmap-spmi.c=189=static int regmap_spmi_ext_write(void *context, const void *data,\n--\ndrivers/base/regmap/regmap-spmi.c-196-\ndrivers/base/regmap/regmap-spmi.c:197:static const struct regmap_bus regmap_spmi_ext = {\ndrivers/base/regmap/regmap-spmi.c-198-\t.read\t\t\t\t= regmap_spmi_ext_read,\n--\ndrivers/base/regmap/regmap-w1.c=146=static int w1_reg_a16_v16_write(void *context, unsigned int reg,\n--\ndrivers/base/regmap/regmap-w1.c-174-\ndrivers/base/regmap/regmap-w1.c:175:static const struct regmap_bus regmap_w1_bus_a8_v8 = {\ndrivers/base/regmap/regmap-w1.c-176-\t.reg_read = w1_reg_a8_v8_read,\n--\ndrivers/base/regmap/regmap-w1.c-179-\ndrivers/base/regmap/regmap-w1.c:180:static const struct regmap_bus regmap_w1_bus_a8_v16 = {\ndrivers/base/regmap/regmap-w1.c-181-\t.reg_read = w1_reg_a8_v16_read,\n--\ndrivers/base/regmap/regmap-w1.c-184-\ndrivers/base/regmap/regmap-w1.c:185:static const struct regmap_bus regmap_w1_bus_a16_v16 = {\ndrivers/base/regmap/regmap-w1.c-186-\t.reg_read = w1_reg_a16_v16_read,\n--\ndrivers/base/regmap/regmap-w1.c-189-\ndrivers/base/regmap/regmap-w1.c:190:static const struct regmap_bus *regmap_get_w1_bus(struct device *w1_dev,\ndrivers/base/regmap/regmap-w1.c-191-\t\t\t\t\tconst struct regmap_config *config)\n--\ndrivers/base/regmap/regmap-w1.c=205=struct regmap *__regmap_init_w1(struct device *w1_dev,\n--\ndrivers/base/regmap/regmap-w1.c-210-\ndrivers/base/regmap/regmap-w1.c:211:\tconst struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config);\ndrivers/base/regmap/regmap-w1.c-212-\n--\ndrivers/base/regmap/regmap-w1.c=221=struct regmap *__devm_regmap_init_w1(struct device *w1_dev,\n--\ndrivers/base/regmap/regmap-w1.c-226-\ndrivers/base/regmap/regmap-w1.c:227:\tconst struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config);\ndrivers/base/regmap/regmap-w1.c-228-\n--\ndrivers/base/regmap/regmap.c=45=static int _regmap_update_bits(struct regmap *map, unsigned int reg,\n--\ndrivers/base/regmap/regmap.c-48-\ndrivers/base/regmap/regmap.c:49:static int _regmap_bus_reg_read(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-50-\t\t\t\tunsigned int *val);\ndrivers/base/regmap/regmap.c:51:static int _regmap_bus_read(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-52-\t\t\t    unsigned int *val);\ndrivers/base/regmap/regmap.c:53:static int _regmap_bus_formatted_write(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-54-\t\t\t\t       unsigned int val);\ndrivers/base/regmap/regmap.c:55:static int _regmap_bus_reg_write(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-56-\t\t\t\t unsigned int val);\ndrivers/base/regmap/regmap.c:57:static int _regmap_bus_raw_write(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-58-\t\t\t\t unsigned int val);\n--\ndrivers/base/regmap/regmap.c=605=static int regmap_detach_dev(struct device *dev, struct regmap *map)\n--\ndrivers/base/regmap/regmap.c-613-\ndrivers/base/regmap/regmap.c:614:static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,\ndrivers/base/regmap/regmap.c-615-\t\t\t\t\tconst struct regmap_config *config)\n--\ndrivers/base/regmap/regmap.c=638=enum regmap_endian regmap_get_val_endian(struct device *dev,\ndrivers/base/regmap/regmap.c:639:\t\t\t\t\t const struct regmap_bus *bus,\ndrivers/base/regmap/regmap.c-640-\t\t\t\t\t const struct regmap_config *config)\n--\ndrivers/base/regmap/regmap.c=677=struct regmap *__regmap_init(struct device *dev,\ndrivers/base/regmap/regmap.c:678:\t\t\t     const struct regmap_bus *bus,\ndrivers/base/regmap/regmap.c-679-\t\t\t     void *bus_context,\n--\ndrivers/base/regmap/regmap.c-833-\tif (config-\u003eread \u0026\u0026 config-\u003ewrite) {\ndrivers/base/regmap/regmap.c:834:\t\tmap-\u003ereg_read  = _regmap_bus_read;\ndrivers/base/regmap/regmap.c-835-\t\tif (config-\u003ereg_update_bits)\n--\ndrivers/base/regmap/regmap.c-851-\t} else if (!bus-\u003eread || !bus-\u003ewrite) {\ndrivers/base/regmap/regmap.c:852:\t\tmap-\u003ereg_read = _regmap_bus_reg_read;\ndrivers/base/regmap/regmap.c:853:\t\tmap-\u003ereg_write = _regmap_bus_reg_write;\ndrivers/base/regmap/regmap.c-854-\t\tmap-\u003ereg_update_bits = bus-\u003ereg_update_bits;\n--\ndrivers/base/regmap/regmap.c-858-\t} else {\ndrivers/base/regmap/regmap.c:859:\t\tmap-\u003ereg_read  = _regmap_bus_read;\ndrivers/base/regmap/regmap.c-860-\t\tmap-\u003ereg_update_bits = bus-\u003ereg_update_bits;\n--\ndrivers/base/regmap/regmap.c-1052-\t\tmap-\u003edefer_caching = false;\ndrivers/base/regmap/regmap.c:1053:\t\tmap-\u003ereg_write = _regmap_bus_formatted_write;\ndrivers/base/regmap/regmap.c-1054-\t} else if (map-\u003eformat.format_val) {\ndrivers/base/regmap/regmap.c-1055-\t\tmap-\u003edefer_caching = true;\ndrivers/base/regmap/regmap.c:1056:\t\tmap-\u003ereg_write = _regmap_bus_raw_write;\ndrivers/base/regmap/regmap.c-1057-\t}\n--\ndrivers/base/regmap/regmap.c=1190=struct regmap *__devm_regmap_init(struct device *dev,\ndrivers/base/regmap/regmap.c:1191:\t\t\t\t  const struct regmap_bus *bus,\ndrivers/base/regmap/regmap.c-1192-\t\t\t\t  void *bus_context,\n--\ndrivers/base/regmap/regmap.c=1868=EXPORT_SYMBOL_GPL(regmap_get_raw_write_max);\ndrivers/base/regmap/regmap.c-1869-\ndrivers/base/regmap/regmap.c:1870:static int _regmap_bus_formatted_write(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-1871-\t\t\t\t       unsigned int val)\n--\ndrivers/base/regmap/regmap.c-1897-\ndrivers/base/regmap/regmap.c:1898:static int _regmap_bus_reg_write(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-1899-\t\t\t\t unsigned int val)\n--\ndrivers/base/regmap/regmap.c-1915-\ndrivers/base/regmap/regmap.c:1916:static int _regmap_bus_raw_write(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-1917-\t\t\t\t unsigned int val)\n--\ndrivers/base/regmap/regmap.c=2754=static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,\n--\ndrivers/base/regmap/regmap.c-2785-\ndrivers/base/regmap/regmap.c:2786:static int _regmap_bus_reg_read(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-2787-\t\t\t\tunsigned int *val)\n--\ndrivers/base/regmap/regmap.c-2803-\ndrivers/base/regmap/regmap.c:2804:static int _regmap_bus_read(void *context, unsigned int reg,\ndrivers/base/regmap/regmap.c-2805-\t\t\t    unsigned int *val)\n\n"
}

11/2 2026/07/26 08:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 52543
							output: 32
							thoughts: 256
						

Thoughts:
**Assessing the Worthiness of Fuzzing a Patch**

Okay, so the prompt tells me to set `WorthFuzzing=false` under specific circumstances. My initial focus is on whether the patch touches code inaccessible in virtualized environments like GCE or QEMU. Specifically, it's about code that relies on hardware not emulated in the usual QEMU setup.

Right now, I'm zeroing in on s390 AP crypto hardware. The question is, is this kind of thing emulated by QEMU? I know about `vfio-ap`, which is pass-through, meaning it needs a physical card on the host. But what about *software* emulation? Does QEMU offer that? I need to clarify that point.

Let me think… Time to check for any software emulation of AP crypto cards within either the Linux kernel itself, or within QEMU's code. In the kernel, I recall seeing `drivers/s390/crypto/ap_bus.c` – that's the AP bus driver. Now, the key is to determine whether it *includes* software emulation. Let’s search for something like “ap_bus” and “emulate” together. That should lead me to the right information.



12/2 2026/07/26 08:49 tool grepper
0m Args:
{
  "Expression": "ap_bus",
  "PathPrefix": "drivers/s390/crypto/"
}

Results:
{
  "Output": "drivers/s390/crypto/Makefile-5-\ndrivers/s390/crypto/Makefile:6:ap-objs := ap_bus.o ap_card.o ap_queue.o\ndrivers/s390/crypto/Makefile-7-obj-$(CONFIG_AP) += ap.o\n--\ndrivers/s390/crypto/ap_bus.c-46-\ndrivers/s390/crypto/ap_bus.c:47:#include \"ap_bus.h\"\ndrivers/s390/crypto/ap_bus.c-48-#include \"ap_debug.h\"\n--\ndrivers/s390/crypto/ap_bus.c=175=static int ap_max_adapter_id = 63;\ndrivers/s390/crypto/ap_bus.c-176-\ndrivers/s390/crypto/ap_bus.c:177:static const struct bus_type ap_bus_type;\ndrivers/s390/crypto/ap_bus.c-178-\n--\ndrivers/s390/crypto/ap_bus.c=622=EXPORT_SYMBOL(ap_release_apmsg);\n--\ndrivers/s390/crypto/ap_bus.c-624-/**\ndrivers/s390/crypto/ap_bus.c:625: * ap_bus_match()\ndrivers/s390/crypto/ap_bus.c-626- * @dev: Pointer to device\n--\ndrivers/s390/crypto/ap_bus.c-630- */\ndrivers/s390/crypto/ap_bus.c:631:static int ap_bus_match(struct device *dev, const struct device_driver *drv)\ndrivers/s390/crypto/ap_bus.c-632-{\n--\ndrivers/s390/crypto/ap_bus.c=804=static void ap_calc_bound_apqns(unsigned int *apqns, unsigned int *bound)\n--\ndrivers/s390/crypto/ap_bus.c-808-\tmemset(\u0026ctrs, 0, sizeof(ctrs));\ndrivers/s390/crypto/ap_bus.c:809:\tbus_for_each_dev(\u0026ap_bus_type, NULL, (void *)\u0026ctrs, __ap_calc_helper);\ndrivers/s390/crypto/ap_bus.c-810-\n--\ndrivers/s390/crypto/ap_bus.c=877=static int __ap_revise_reserved(struct device *dev, void *dummy)\n--\ndrivers/s390/crypto/ap_bus.c-918-\ndrivers/s390/crypto/ap_bus.c:919:static void ap_bus_revise_bindings(void)\ndrivers/s390/crypto/ap_bus.c-920-{\ndrivers/s390/crypto/ap_bus.c:921:\tbus_for_each_dev(\u0026ap_bus_type, NULL, NULL, __ap_revise_reserved);\ndrivers/s390/crypto/ap_bus.c-922-}\n--\ndrivers/s390/crypto/ap_bus.c=1108=int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,\n--\ndrivers/s390/crypto/ap_bus.c-1113-\ndrivers/s390/crypto/ap_bus.c:1114:\tdrv-\u003ebus = \u0026ap_bus_type;\ndrivers/s390/crypto/ap_bus.c-1115-\tdrv-\u003eowner = owner;\n--\ndrivers/s390/crypto/ap_bus.c=1129=EXPORT_SYMBOL(ap_driver_unregister);\n--\ndrivers/s390/crypto/ap_bus.c-1135- */\ndrivers/s390/crypto/ap_bus.c:1136:bool ap_bus_force_rescan(void)\ndrivers/s390/crypto/ap_bus.c-1137-{\n--\ndrivers/s390/crypto/ap_bus.c-1150-\t * all cards are offline so a rescan is triggered which causes\ndrivers/s390/crypto/ap_bus.c:1151:\t * a recursive call of ap_bus_force_rescan(). A simple return if\ndrivers/s390/crypto/ap_bus.c-1152-\t * the mutex is already locked by this thread solves this.\n--\ndrivers/s390/crypto/ap_bus.c-1186-}\ndrivers/s390/crypto/ap_bus.c:1187:EXPORT_SYMBOL(ap_bus_force_rescan);\ndrivers/s390/crypto/ap_bus.c-1188-\n--\ndrivers/s390/crypto/ap_bus.c-1191- */\ndrivers/s390/crypto/ap_bus.c:1192:static int ap_bus_cfg_chg(struct notifier_block *nb,\ndrivers/s390/crypto/ap_bus.c-1193-\t\t\t  unsigned long action, void *data)\n--\ndrivers/s390/crypto/ap_bus.c-1199-\ndrivers/s390/crypto/ap_bus.c:1200:\tap_bus_force_rescan();\ndrivers/s390/crypto/ap_bus.c-1201-\n--\ndrivers/s390/crypto/ap_bus.c-1204-\ndrivers/s390/crypto/ap_bus.c:1205:static struct notifier_block ap_bus_nb = {\ndrivers/s390/crypto/ap_bus.c:1206:\t.notifier_call = ap_bus_cfg_chg,\ndrivers/s390/crypto/ap_bus.c-1207-};\n--\ndrivers/s390/crypto/ap_bus.c=1549=static int apmask_commit(unsigned long *newapm)\n--\ndrivers/s390/crypto/ap_bus.c-1558-\tif (bitmap_andnot(reserved, newapm, ap_perms.apm, AP_DEVICES)) {\ndrivers/s390/crypto/ap_bus.c:1559:\t\trc = bus_for_each_drv(\u0026ap_bus_type, NULL, reserved,\ndrivers/s390/crypto/ap_bus.c-1560-\t\t\t\t      __verify_card_reservations);\n--\ndrivers/s390/crypto/ap_bus.c=1579=static ssize_t apmask_store(const struct bus_type *bus, const char *buf,\n--\ndrivers/s390/crypto/ap_bus.c-1605-\tif (changes) {\ndrivers/s390/crypto/ap_bus.c:1606:\t\tap_bus_revise_bindings();\ndrivers/s390/crypto/ap_bus.c-1607-\t\tap_send_mask_changed_uevent(newapm, NULL);\n--\ndrivers/s390/crypto/ap_bus.c=1656=static int aqmask_commit(unsigned long *newaqm)\n--\ndrivers/s390/crypto/ap_bus.c-1665-\tif (bitmap_andnot(reserved, newaqm, ap_perms.aqm, AP_DOMAINS)) {\ndrivers/s390/crypto/ap_bus.c:1666:\t\trc = bus_for_each_drv(\u0026ap_bus_type, NULL, reserved,\ndrivers/s390/crypto/ap_bus.c-1667-\t\t\t\t      __verify_queue_reservations);\n--\ndrivers/s390/crypto/ap_bus.c=1686=static ssize_t aqmask_store(const struct bus_type *bus, const char *buf,\n--\ndrivers/s390/crypto/ap_bus.c-1712-\tif (changes) {\ndrivers/s390/crypto/ap_bus.c:1713:\t\tap_bus_revise_bindings();\ndrivers/s390/crypto/ap_bus.c-1714-\t\tap_send_mask_changed_uevent(NULL, newaqm);\n--\ndrivers/s390/crypto/ap_bus.c=1727=static ssize_t scans_store(const struct bus_type *bus, const char *buf,\n--\ndrivers/s390/crypto/ap_bus.c-1731-\ndrivers/s390/crypto/ap_bus.c:1732:\tap_bus_force_rescan();\ndrivers/s390/crypto/ap_bus.c-1733-\n--\ndrivers/s390/crypto/ap_bus.c=1787=static BUS_ATTR_RO(features);\ndrivers/s390/crypto/ap_bus.c-1788-\ndrivers/s390/crypto/ap_bus.c:1789:static struct attribute *ap_bus_attrs[] = {\ndrivers/s390/crypto/ap_bus.c-1790-\t\u0026bus_attr_ap_domain.attr,\n--\ndrivers/s390/crypto/ap_bus.c-1807-};\ndrivers/s390/crypto/ap_bus.c:1808:ATTRIBUTE_GROUPS(ap_bus);\ndrivers/s390/crypto/ap_bus.c-1809-\ndrivers/s390/crypto/ap_bus.c:1810:static const struct bus_type ap_bus_type = {\ndrivers/s390/crypto/ap_bus.c-1811-\t.name = \"ap\",\ndrivers/s390/crypto/ap_bus.c:1812:\t.bus_groups = ap_bus_groups,\ndrivers/s390/crypto/ap_bus.c:1813:\t.match = \u0026ap_bus_match,\ndrivers/s390/crypto/ap_bus.c-1814-\t.uevent = \u0026ap_uevent,\n--\ndrivers/s390/crypto/ap_bus.c=1954=static inline void notify_config_changed(void)\ndrivers/s390/crypto/ap_bus.c-1955-{\ndrivers/s390/crypto/ap_bus.c:1956:\tbus_for_each_drv(\u0026ap_bus_type, NULL, NULL,\ndrivers/s390/crypto/ap_bus.c-1957-\t\t\t __drv_notify_config_changed);\n--\ndrivers/s390/crypto/ap_bus.c=1976=static inline void notify_scan_complete(void)\ndrivers/s390/crypto/ap_bus.c-1977-{\ndrivers/s390/crypto/ap_bus.c:1978:\tbus_for_each_drv(\u0026ap_bus_type, NULL, NULL,\ndrivers/s390/crypto/ap_bus.c-1979-\t\t\t __drv_notify_scan_complete);\n--\ndrivers/s390/crypto/ap_bus.c=1986=static inline void ap_scan_rm_card_dev_and_queue_devs(struct ap_card *ac)\ndrivers/s390/crypto/ap_bus.c-1987-{\ndrivers/s390/crypto/ap_bus.c:1988:\tbus_for_each_dev(\u0026ap_bus_type, NULL,\ndrivers/s390/crypto/ap_bus.c-1989-\t\t\t (void *)(long)ac-\u003eid,\n--\ndrivers/s390/crypto/ap_bus.c=1999=static inline void ap_scan_domains(struct ap_card *ac)\n--\ndrivers/s390/crypto/ap_bus.c-2015-\t\tqid = AP_MKQID(ac-\u003eid, dom);\ndrivers/s390/crypto/ap_bus.c:2016:\t\tdev = bus_find_device(\u0026ap_bus_type, NULL,\ndrivers/s390/crypto/ap_bus.c-2017-\t\t\t\t      (void *)(long)qid,\n--\ndrivers/s390/crypto/ap_bus.c-2054-\t\t\tdev = \u0026aq-\u003eap_dev.device;\ndrivers/s390/crypto/ap_bus.c:2055:\t\t\tdev-\u003ebus = \u0026ap_bus_type;\ndrivers/s390/crypto/ap_bus.c-2056-\t\t\tdev-\u003eparent = \u0026ac-\u003eap_dev.device;\n--\ndrivers/s390/crypto/ap_bus.c=2156=static inline void ap_scan_adapter(int ap)\n--\ndrivers/s390/crypto/ap_bus.c-2165-\t/* Is there currently a card device for this adapter ? */\ndrivers/s390/crypto/ap_bus.c:2166:\tdev = bus_find_device(\u0026ap_bus_type, NULL,\ndrivers/s390/crypto/ap_bus.c-2167-\t\t\t      (void *)(long)ap,\n--\ndrivers/s390/crypto/ap_bus.c-2279-\t\tdev = \u0026ac-\u003eap_dev.device;\ndrivers/s390/crypto/ap_bus.c:2280:\t\tdev-\u003ebus = \u0026ap_bus_type;\ndrivers/s390/crypto/ap_bus.c-2281-\t\tdev-\u003eparent = ap_root_device;\n--\ndrivers/s390/crypto/ap_bus.c=2390=static bool ap_scan_bus(void)\n--\ndrivers/s390/crypto/ap_bus.c-2424-\t\tstruct device *dev =\ndrivers/s390/crypto/ap_bus.c:2425:\t\t\tbus_find_device(\u0026ap_bus_type, NULL,\ndrivers/s390/crypto/ap_bus.c-2426-\t\t\t\t\t(void *)(long)ap_domain_index,\n--\ndrivers/s390/crypto/ap_bus.c=2484=static inline void __exit ap_async_exit(void)\n--\ndrivers/s390/crypto/ap_bus.c-2487-\t\tap_poll_thread_stop();\ndrivers/s390/crypto/ap_bus.c:2488:\tchsc_notifier_unregister(\u0026ap_bus_nb);\ndrivers/s390/crypto/ap_bus.c-2489-\tcancel_work(\u0026ap_scan_bus_work);\n--\ndrivers/s390/crypto/ap_bus.c=2494=static inline int __init ap_async_init(void)\n--\ndrivers/s390/crypto/ap_bus.c-2510-\ndrivers/s390/crypto/ap_bus.c:2511:\trc = chsc_notifier_register(\u0026ap_bus_nb);\ndrivers/s390/crypto/ap_bus.c-2512-\tif (rc)\n--\ndrivers/s390/crypto/ap_bus.c-2525-out_notifier:\ndrivers/s390/crypto/ap_bus.c:2526:\tchsc_notifier_unregister(\u0026ap_bus_nb);\ndrivers/s390/crypto/ap_bus.c-2527-out:\n--\ndrivers/s390/crypto/ap_bus.c=2595=static int __init ap_module_init(void)\n--\ndrivers/s390/crypto/ap_bus.c-2634-\t/* Create /sys/bus/ap. */\ndrivers/s390/crypto/ap_bus.c:2635:\trc = bus_register(\u0026ap_bus_type);\ndrivers/s390/crypto/ap_bus.c-2636-\tif (rc)\n--\ndrivers/s390/crypto/ap_bus.c-2643-\t\tgoto out_bus;\ndrivers/s390/crypto/ap_bus.c:2644:\tap_root_device-\u003ebus = \u0026ap_bus_type;\ndrivers/s390/crypto/ap_bus.c-2645-\n--\ndrivers/s390/crypto/ap_bus.c-2662-out_bus:\ndrivers/s390/crypto/ap_bus.c:2663:\tbus_unregister(\u0026ap_bus_type);\ndrivers/s390/crypto/ap_bus.c-2664-out:\n--\ndrivers/s390/crypto/ap_bus.c=2670=static void __exit ap_module_exit(void)\n--\ndrivers/s390/crypto/ap_bus.c-2674-\troot_device_unregister(ap_root_device);\ndrivers/s390/crypto/ap_bus.c:2675:\tbus_unregister(\u0026ap_bus_type);\ndrivers/s390/crypto/ap_bus.c-2676-\tmempool_destroy(ap_msg_pool);\n--\ndrivers/s390/crypto/ap_bus.h=257=void ap_request_timeout(struct timer_list *t);\ndrivers/s390/crypto/ap_bus.h:258:bool ap_bus_force_rescan(void);\ndrivers/s390/crypto/ap_bus.h-259-\n--\ndrivers/s390/crypto/ap_card.c-15-\ndrivers/s390/crypto/ap_card.c:16:#include \"ap_bus.h\"\ndrivers/s390/crypto/ap_card.c-17-\n--\ndrivers/s390/crypto/ap_queue.c-18-\ndrivers/s390/crypto/ap_queue.c:19:#include \"ap_bus.h\"\ndrivers/s390/crypto/ap_queue.c-20-#include \"ap_debug.h\"\n--\ndrivers/s390/crypto/vfio_ap_private.h-23-\ndrivers/s390/crypto/vfio_ap_private.h:24:#include \"ap_bus.h\"\ndrivers/s390/crypto/vfio_ap_private.h-25-\n--\ndrivers/s390/crypto/zcrypt_api.c=80=static inline bool zcrypt_process_rescan(void)\ndrivers/s390/crypto/zcrypt_api.c-81-{\ndrivers/s390/crypto/zcrypt_api.c:82:\treturn ap_bus_force_rescan();\ndrivers/s390/crypto/zcrypt_api.c-83-}\n--\ndrivers/s390/crypto/zcrypt_api.h-19-#include \u003casm/zcrypt.h\u003e\ndrivers/s390/crypto/zcrypt_api.h:20:#include \"ap_bus.h\"\ndrivers/s390/crypto/zcrypt_api.h-21-\n--\ndrivers/s390/crypto/zcrypt_ccamisc.c-20-\ndrivers/s390/crypto/zcrypt_ccamisc.c:21:#include \"ap_bus.h\"\ndrivers/s390/crypto/zcrypt_ccamisc.c-22-#include \"zcrypt_api.h\"\n--\ndrivers/s390/crypto/zcrypt_cex4.c-15-\ndrivers/s390/crypto/zcrypt_cex4.c:16:#include \"ap_bus.h\"\ndrivers/s390/crypto/zcrypt_cex4.c-17-#include \"zcrypt_api.h\"\n--\ndrivers/s390/crypto/zcrypt_ep11misc.c-20-\ndrivers/s390/crypto/zcrypt_ep11misc.c:21:#include \"ap_bus.h\"\ndrivers/s390/crypto/zcrypt_ep11misc.c-22-#include \"zcrypt_api.h\"\n--\ndrivers/s390/crypto/zcrypt_msgtype50.c-21-\ndrivers/s390/crypto/zcrypt_msgtype50.c:22:#include \"ap_bus.h\"\ndrivers/s390/crypto/zcrypt_msgtype50.c-23-#include \"zcrypt_api.h\"\n--\ndrivers/s390/crypto/zcrypt_msgtype6.c-22-\ndrivers/s390/crypto/zcrypt_msgtype6.c:23:#include \"ap_bus.h\"\ndrivers/s390/crypto/zcrypt_msgtype6.c-24-#include \"zcrypt_api.h\"\n"
}

13/2 2026/07/26 08:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 58388
							output: 22
							thoughts: 210
						

Thoughts:
**My Current Understanding of the vfio-ap Driver and mdev**

Okay, so I'm trying to wrap my head around this `vfio-ap` stuff and how it interacts with the underlying hardware, specifically the S390x AP bus. Right, the `ap_bus` relies on these `ap_instructions`, things like `ap_tapq`, that are specific to the hardware. If QEMU doesn't fully emulate these instructions, the whole AP bus discovery will fail. That means the `vfio_ap` driver won't ever see any devices. But wait a minute... isn't `vfio_ap` actually an mdev driver?

If it's an mdev driver, then I should be able to create an mdev device, even if there aren't *physical* AP queues to back it. That's a key distinction! Let me double-check the code... I should start by looking at `vfio_ap_mdev_probe` and then `vfio_ap_matrix_driver` to see if I'm on the right track.

Okay, yep, `vfio_ap_matrix_driver` *is* registered with `mdev_register_driver`. That confirms the mdev aspect. So, the userspace interface for creating an mdev device is writing to that `/sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/create` file. Ah, the "matrix" part is a bit confusing initially. It turns out that `matrix` is the *parent* device, but where does that parent device *get created*? That's the crucial missing piece. I need to figure out how that parent device comes into existence.



14/2 2026/07/26 08:50 tool grepper
0m Args:
{
  "Expression": "matrix_dev.*=.*kzalloc"
}

Results:
{
  "Output": "drivers/s390/crypto/vfio_ap_drv.c=88=static int vfio_ap_matrix_dev_create(void)\n--\ndrivers/s390/crypto/vfio_ap_drv.c-100-\ndrivers/s390/crypto/vfio_ap_drv.c:101:\tmatrix_dev = kzalloc_obj(*matrix_dev);\ndrivers/s390/crypto/vfio_ap_drv.c-102-\tif (!matrix_dev) {\n"
}

15/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 59140
							output: 24
							thoughts: 16
						

Thoughts:
Okay, let's dive into this. Here's my thought process on this:

**Analyzing `vfio_ap_matrix_dev_create`**

Alright, the task at hand is to understand `vfio_ap_matrix_dev_create`. First, I'll need to remember where this function is situated within the VFIO (Virtual Function I/O) infrastructure. My initial assumption is that this relates to some aspect of creating or managing a device matrix, likely related to the Advanced Peripheral (AP) functionality. I need to refresh my mental map of how the VFIO driver interacts with the hardware, the relationships between the different structures (e.g., `vfio_device`, `vfio_group`, `iommu_group`), and where the AP extension plays into this architecture. Given that it has `dev` in the name, my intuition is that it might be responsible for initializing or associating a new AP device with the VFIO framework. I'll need to figure out the parameters. What data structures does it accept? And, what data structures does it return. What does it return if it returns nothing? Is there a status code or error code that must be checked. Knowing this will clarify its role in the device lifecycle.



16/2 2026/07/26 08:50 tool grepper
0m Args:
{
  "Expression": "vfio_ap_matrix_dev_create"
}

Results:
{
  "Output": "drivers/s390/crypto/vfio_ap_drv.c=81=static struct device_driver matrix_driver = {\n--\ndrivers/s390/crypto/vfio_ap_drv.c-87-\ndrivers/s390/crypto/vfio_ap_drv.c:88:static int vfio_ap_matrix_dev_create(void)\ndrivers/s390/crypto/vfio_ap_drv.c-89-{\n--\ndrivers/s390/crypto/vfio_ap_drv.c=169=static int __init vfio_ap_init(void)\n--\ndrivers/s390/crypto/vfio_ap_drv.c-180-\ndrivers/s390/crypto/vfio_ap_drv.c:181:\tret = vfio_ap_matrix_dev_create();\ndrivers/s390/crypto/vfio_ap_drv.c-182-\tif (ret)\n"
}

17/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 59697
							output: 22
							thoughts: 192
						

Thoughts:
**My Initial Thoughts on VFIO-AP Migration**

Okay, so `vfio_ap_matrix_dev_create` is called right at module initialization – that's the starting point. It's creating that root device, `/sys/devices/vfio_ap/matrix`.  And then it's registering the mdev parent, which, if I'm understanding this correctly, means that userspace always has the ability to create a `vfio_ap-passthrough` mdev just by writing a UUID to the `create` file.  Pretty straightforward setup, really. Once that mdev is instantiated, VFIO can handle the opening process.

The addition of migration support, as presented by the patch, is very interesting, and the exposure via `vfio_ap_migration_ops` seems logical.  But here's what's got me thinking: can we, as userspace developers, inadvertently trigger those migration code paths – specifically, `vfio_ap_set_state`, `vfio_ap_stop_copy_read`, and `vfio_ap_resuming_write` – even *without* any actual physical AP queues being present?  That's a potentially crucial vulnerability to explore.  Let's start by digging into `vfio_ap_set_state` and see how this works.



18/2 2026/07/26 08:50 tool grepper
0m Args:
{
  "Expression": "vfio_ap_set_state"
}

Results:
{
  "Output": "drivers/s390/crypto/vfio_ap_migration.c=1261=vfio_ap_transition_to_state(struct ap_matrix_mdev *matrix_mdev,\n--\ndrivers/s390/crypto/vfio_ap_migration.c-1338-\ndrivers/s390/crypto/vfio_ap_migration.c:1339:static struct file *vfio_ap_set_state(struct vfio_device *vdev,\ndrivers/s390/crypto/vfio_ap_migration.c-1340-\t\t\t\t      enum vfio_device_mig_state  new_state)\n--\ndrivers/s390/crypto/vfio_ap_migration.c=1446=static const struct vfio_migration_ops vfio_ap_migration_ops = {\ndrivers/s390/crypto/vfio_ap_migration.c:1447:\t.migration_set_state = vfio_ap_set_state,\ndrivers/s390/crypto/vfio_ap_migration.c-1448-\t.migration_get_state = vfio_ap_get_state,\n"
}

19/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 60495
							output: 47
							thoughts: 34
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach that kernel code:

**Diving into `vfio_ap_set_state`**

Right, let's take a look at `vfio_ap_set_state`. Specifically, I need to get into the heart of `drivers/s390/crypto/vfio_ap_migration.c`. That's where the interesting stuff for the VFIO-AP migration logic should be. I'm expecting this function to be the central point for managing the state of the AP devices within a VFIO environment, especially during a migration. It likely handles the intricate details of what needs to change to get an AP device ready, either on the source or destination, or in between. Depending on the design, I'm expecting to see state machine logic here, resource allocation, and device command sending to the hardware AP. I anticipate there will be memory barriers to consider, locking structures to look at and of course error handling to understand how it's done. I'm going in with the expectation of seeing a robust mechanism for ensuring the integrity of the crypto operations throughout this process.



20/2 2026/07/26 08:50 tool read-file
0m Args:
{
  "File": "drivers/s390/crypto/vfio_ap_migration.c",
  "FirstLine": 1339,
  "LineCount": 100
}

Results:
{
  "Contents": "1339:\tstatic struct file *vfio_ap_set_state(struct vfio_device *vdev,\n1340:\t\t\t\t\t      enum vfio_device_mig_state  new_state)\n1341:\t{\n1342:\t\tint ret;\n1343:\t\tstruct file *filp = NULL;\n1344:\t\tstruct ap_matrix_mdev *matrix_mdev;\n1345:\t\tenum vfio_device_mig_state next_state;\n1346:\t\tstruct vfio_ap_migration_data *mig_data;\n1347:\t\n1348:\t\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n1349:\t\n1350:\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n1351:\t\tif (ap_is_se_guest()) {\n1352:\t\t\tdev_err_once(matrix_mdev-\u003evdev.dev,\n1353:\t\t\t\t     \"Migration not allowed from or to a Secure Execution guest\\n\");\n1354:\t\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n1355:\t\t\treturn ERR_PTR(-EPERM);\n1356:\t\t}\n1357:\t\n1358:\t\tmig_data = matrix_mdev-\u003emig_data;\n1359:\t\n1360:\t\t/*\n1361:\t\t * The mig_data pointer is set in the vfio_ap_init_migration_data\n1362:\t\t * function which is called when the vfio-ap device fd is opened.\n1363:\t\t * Since the implicit pre-open state is RUNNING, a request to set\n1364:\t\t * RUNNING is a no-op. Any other state transition is invalid before\n1365:\t\t * open_device.\n1366:\t\t */\n1367:\t\tif (!mig_data) {\n1368:\t\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n1369:\t\t\tif (new_state == VFIO_DEVICE_STATE_RUNNING)\n1370:\t\t\t\treturn NULL;\n1371:\t\t\treturn ERR_PTR(-ENODEV);\n1372:\t\t}\n1373:\t\n1374:\t\tdev_dbg(vdev-\u003edev, \"%s -\u003e %d\\n\", __func__, new_state);\n1375:\t\n1376:\t\twhile (mig_data-\u003emig_state != VFIO_DEVICE_STATE_ERROR \u0026\u0026\n1377:\t\t       mig_data-\u003emig_state != new_state) {\n1378:\t\t\tret = vfio_mig_get_next_state(vdev, mig_data-\u003emig_state,\n1379:\t\t\t\t\t\t      new_state, \u0026next_state);\n1380:\t\t\tif (ret) {\n1381:\t\t\t\tfilp = ERR_PTR(ret);\n1382:\t\t\t\tbreak;\n1383:\t\t\t}\n1384:\t\n1385:\t\t\tfilp = vfio_ap_transition_to_state(matrix_mdev, next_state);\n1386:\t\t\tif (IS_ERR(filp))\n1387:\t\t\t\tbreak;\n1388:\t\n1389:\t\t\tmig_data-\u003emig_state = next_state;\n1390:\t\n1391:\t\t\tif (WARN_ON(filp \u0026\u0026 new_state != next_state)) {\n1392:\t\t\t\tfput(filp);\n1393:\t\t\t\tfilp = ERR_PTR(-EINVAL);\n1394:\t\t\t\tbreak;\n1395:\t\t\t}\n1396:\t\t}\n1397:\t\n1398:\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n1399:\t\n1400:\t\treturn filp;\n1401:\t}\n1402:\t\n1403:\tstatic int vfio_ap_get_state(struct vfio_device *vdev,\n1404:\t\t\t\t     enum vfio_device_mig_state  *current_state)\n1405:\t{\n1406:\t\tstruct ap_matrix_mdev *matrix_mdev;\n1407:\t\tstruct vfio_ap_migration_data *mig_data;\n1408:\t\n1409:\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n1410:\t\n1411:\t\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n1412:\t\tmig_data =  matrix_mdev-\u003emig_data;\n1413:\t\n1414:\t\t/*\n1415:\t\t * The mig_data pointer is set in the vfio_ap_init_migration_data\n1416:\t\t * function which is called when the vfio-ap device fd is opened.\n1417:\t\t * If mig_data is NULL, report RUNNING as the implicit pre-open state\n1418:\t\t * so userspace doesn't need to perform any state transition before the\n1419:\t\t * device becomes active.\n1420:\t\t */\n1421:\t\t*current_state = (mig_data) ? mig_data-\u003emig_state :\n1422:\t\t\t\t\t      VFIO_DEVICE_STATE_RUNNING;\n1423:\t\n1424:\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n1425:\t\n1426:\t\treturn 0;\n1427:\t}\n1428:\t\n1429:\tstatic int vfio_ap_get_data_size(struct vfio_device *vdev,\n1430:\t\t\t\t\t unsigned long *stop_copy_length)\n1431:\t{\n1432:\t\tstruct ap_matrix_mdev *matrix_mdev;\n1433:\t\tsize_t qinfo_sz;\n1434:\t\tint num_queues;\n1435:\t\n1436:\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n1437:\t\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n1438:\t\tnum_queues = vfio_ap_mdev_get_num_queues(\u0026matrix_mdev-\u003eshadow_apcb);\n"
}

21/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 62441
							output: 46
							thoughts: 67
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**VFIO Migration State Considerations**

Okay, so I'm looking at how the migration state is managed with VFIO. It seems the key control point is the `VFIO_DEVICE_FEATURE_MIGRATION` ioctl. That's good, that's what I expected. This eventually calls `vfio_ap_set_state`. Makes sense.

Now, hold on a second... to even *get* to that ioctl, to interact with the device at all, I'll need to bind it to KVM, right? It's the standard VFIO setup. I need to make sure I'm not missing anything in the device opening process. Let me check the `vfio_ap_mdev_open_device` function. I want to make sure the open path itself is clear. Gotta ensure everything is set up correctly before I start messing with the migration state. Need to verify that the device is correctly prepared and accessible before I even attempt this feature.



22/2 2026/07/26 08:50 tool read-file
0m Args:
{
  "File": "drivers/s390/crypto/vfio_ap_ops.c",
  "FirstLine": 2100,
  "LineCount": 50
}

Results:
{
  "Contents": "2100:\t\treturn ret;\n2101:\t}\n2102:\t\n2103:\tstatic int vfio_ap_mdev_open_device(struct vfio_device *vdev)\n2104:\t{\n2105:\t\tstruct ap_matrix_mdev *matrix_mdev;\n2106:\t\tint ret;\n2107:\t\n2108:\t\tif (!vdev-\u003ekvm)\n2109:\t\t\treturn -EINVAL;\n2110:\t\n2111:\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n2112:\t\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n2113:\t\tret = vfio_ap_init_migration_data(matrix_mdev);\n2114:\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n2115:\t\n2116:\t\tif (ret)\n2117:\t\t\treturn ret;\n2118:\t\n2119:\t\tret = vfio_ap_mdev_set_kvm(matrix_mdev, vdev-\u003ekvm);\n2120:\t\tif (ret) {\n2121:\t\t\t/* Clean up migration data on failure */\n2122:\t\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n2123:\t\t\tvfio_ap_release_migration_data(matrix_mdev);\n2124:\t\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n2125:\t\t}\n2126:\t\n2127:\t\treturn ret;\n2128:\t}\n2129:\t\n2130:\tstatic void vfio_ap_mdev_close_device(struct vfio_device *vdev)\n2131:\t{\n2132:\t\tstruct ap_matrix_mdev *matrix_mdev;\n2133:\t\n2134:\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n2135:\t\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n2136:\t\tvfio_ap_release_migration_data(matrix_mdev);\n2137:\t\tmutex_unlock(\u0026matrix_dev-\u003emdevs_lock);\n2138:\t\n2139:\t\tvfio_ap_mdev_unset_kvm(matrix_mdev);\n2140:\t}\n2141:\t\n2142:\tstatic void vfio_ap_mdev_request(struct vfio_device *vdev, unsigned int count)\n2143:\t{\n2144:\t\tstruct device *dev = vdev-\u003edev;\n2145:\t\tstruct ap_matrix_mdev *matrix_mdev;\n2146:\t\n2147:\t\tmatrix_mdev = container_of(vdev, struct ap_matrix_mdev, vdev);\n2148:\t\n2149:\t\tget_update_locks_for_mdev(matrix_mdev);\n"
}

23/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 63574
							output: 26
							thoughts: 127
						

Thoughts:
**Analyzing the vfio_ap_mdev_open_device Function**

Okay, so I'm looking at `vfio_ap_mdev_open_device` and it's immediately clear that this function needs `vdev->kvm` to be populated. That tells me the device has to be opened by a KVM process, something like QEMU, that's linking a KVM file descriptor to the VFIO group. Makes sense. Now, does syzkaller play in that arena? I know it does a ton of KVM fuzzing, so that shouldn't be a problem. Wait a minute... there might be more to it. There's also `vfio_ap_mdev_set_kvm`. Does that have s390 KVM requirements? It might. I should probably dig into that function and see what's what before I go further. Let's check `vfio_ap_mdev_set_kvm`.



24/2 2026/07/26 08:50 tool grepper
0m Args:
{
  "Expression": "vfio_ap_mdev_set_kvm"
}

Results:
{
  "Output": "drivers/s390/crypto/vfio_ap_ops.c=1856=static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {\n--\ndrivers/s390/crypto/vfio_ap_ops.c-1861-/**\ndrivers/s390/crypto/vfio_ap_ops.c:1862: * vfio_ap_mdev_set_kvm - sets all data for @matrix_mdev that are needed\ndrivers/s390/crypto/vfio_ap_ops.c-1863- * to manage AP resources for the guest whose state is represented by @kvm\n--\ndrivers/s390/crypto/vfio_ap_ops.c-1870- */\ndrivers/s390/crypto/vfio_ap_ops.c:1871:static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,\ndrivers/s390/crypto/vfio_ap_ops.c-1872-\t\t\t\tstruct kvm *kvm)\n--\ndrivers/s390/crypto/vfio_ap_ops.c=2103=static int vfio_ap_mdev_open_device(struct vfio_device *vdev)\n--\ndrivers/s390/crypto/vfio_ap_ops.c-2118-\ndrivers/s390/crypto/vfio_ap_ops.c:2119:\tret = vfio_ap_mdev_set_kvm(matrix_mdev, vdev-\u003ekvm);\ndrivers/s390/crypto/vfio_ap_ops.c-2120-\tif (ret) {\n"
}

25/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 64378
							output: 46
							thoughts: 18
						

Thoughts:
Okay, let's dive into `vfio_ap_mdev_set_kvm`.

**My Initial Assessment of `vfio_ap_mdev_set_kvm`**

Alright, here we go. The name immediately suggests this function deals with setting up a KVM-related aspect, specifically related to the Advanced Peripheral (AP) mechanism within VFIO's mdev (mediated device) framework. Knowing my area of expertise, this hints at low-level hardware virtualization, probably involving direct device assignment. I'm expecting to see operations that touch upon the core elements of assigning an AP device to a KVM guest. It probably involves resource mapping, interrupt handling setup, and potentially access control lists or permissioning, all to ensure the guest can safely and directly interact with this AP device through VFIO. I need to understand how the AP device's state is integrated with the KVM guest's execution flow. I'm also curious about what locking mechanisms are employed to ensure concurrency safety, given the multithreaded nature of KVM and VFIO. I anticipate encountering data structures specific to mdev and AP devices, so a refresher on their structure definitions might be helpful. Let's see how well it's implemented.



26/2 2026/07/26 08:50 tool read-file
0m Args:
{
  "File": "drivers/s390/crypto/vfio_ap_ops.c",
  "FirstLine": 1871,
  "LineCount": 50
}

Results:
{
  "Contents": "1871:\tstatic int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,\n1872:\t\t\t\t\tstruct kvm *kvm)\n1873:\t{\n1874:\t\tstruct ap_matrix_mdev *m;\n1875:\t\n1876:\t\tif (kvm-\u003earch.crypto.crycbd) {\n1877:\t\t\tdown_write(\u0026kvm-\u003earch.crypto.pqap_hook_rwsem);\n1878:\t\t\tkvm-\u003earch.crypto.pqap_hook = \u0026matrix_mdev-\u003epqap_hook;\n1879:\t\t\tup_write(\u0026kvm-\u003earch.crypto.pqap_hook_rwsem);\n1880:\t\n1881:\t\t\tget_update_locks_for_kvm(kvm);\n1882:\t\n1883:\t\t\tlist_for_each_entry(m, \u0026matrix_dev-\u003emdev_list, node) {\n1884:\t\t\t\tif (m != matrix_mdev \u0026\u0026 m-\u003ekvm == kvm) {\n1885:\t\t\t\t\trelease_update_locks_for_kvm(kvm);\n1886:\t\t\t\t\treturn -EPERM;\n1887:\t\t\t\t}\n1888:\t\t\t}\n1889:\t\n1890:\t\t\tkvm_get_kvm(kvm);\n1891:\t\t\tmatrix_mdev-\u003ekvm = kvm;\n1892:\t\t\tvfio_ap_mdev_update_guest_apcb(matrix_mdev);\n1893:\t\n1894:\t\t\trelease_update_locks_for_kvm(kvm);\n1895:\t\t}\n1896:\t\n1897:\t\treturn 0;\n1898:\t}\n1899:\t\n1900:\tstatic void unmap_iova(struct ap_matrix_mdev *matrix_mdev, u64 iova, u64 length)\n1901:\t{\n1902:\t\tstruct ap_queue_table *qtable = \u0026matrix_mdev-\u003eqtable;\n1903:\t\tstruct vfio_ap_queue *q;\n1904:\t\tint loop_cursor;\n1905:\t\n1906:\t\thash_for_each(qtable-\u003equeues, loop_cursor, q, mdev_qnode) {\n1907:\t\t\tif (q-\u003esaved_iova \u003e= iova \u0026\u0026 q-\u003esaved_iova \u003c iova + length)\n1908:\t\t\t\tvfio_ap_irq_disable(q);\n1909:\t\t}\n1910:\t}\n1911:\t\n1912:\tstatic void vfio_ap_mdev_dma_unmap(struct vfio_device *vdev, u64 iova,\n1913:\t\t\t\t\t   u64 length)\n1914:\t{\n1915:\t\tstruct ap_matrix_mdev *matrix_mdev =\n1916:\t\t\tcontainer_of(vdev, struct ap_matrix_mdev, vdev);\n1917:\t\n1918:\t\tmutex_lock(\u0026matrix_dev-\u003emdevs_lock);\n1919:\t\n1920:\t\tunmap_iova(matrix_mdev, iova, length);\n"
}

27/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 65553
							output: 19
							thoughts: 135
						

Thoughts:
**VFIO AP and KVM Interaction - A Quick Assessment**

Okay, so `vfio_ap_mdev_set_kvm` seems surprisingly robust. It returns success even when `kvm->arch.crypto.crycbd` is NULL. That means we can successfully open the device even without that specific crypto component fully initialized – interesting. This also means we could potentially utilize the VFIO migration ioctls and trigger the new functionality in `vfio_ap_migration.c`. It sounds promising from an initial point of view.

But, a nagging doubt... is `CONFIG_VFIO_AP` actually enabled? Is it a default or an optional thing for s390? Let's quickly verify. Ah, yes, it's a standard config for s390. I'll need to check the Kconfig file for the crypto driver in `drivers/s390/crypto/Kconfig` to be absolutely certain of the default settings and any dependencies. That should clarify a couple of potential pitfalls, and help me formulate the implications of this feature more effectively.



28/2 2026/07/26 08:50 tool grepper
0m Args:
{
  "Expression": "config VFIO_AP"
}

Results:
{
  "Output": "arch/s390/Kconfig=823=config VFIO_CCW\n--\narch/s390/Kconfig-833-\narch/s390/Kconfig:834:config VFIO_AP\narch/s390/Kconfig-835-\tdef_tristate n\n"
}

29/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 66029
							output: 37
							thoughts: 22
						

Thoughts:
Here's my analysis of the provided text, rendered as a first-person expert summary:

**Diving into the s390 Architecture Configuration**

Okay, so we're looking at the `arch/s390/Kconfig` file, specifically around line 834. I'm going to assume this section likely relates to some feature or configuration option within the s390 architecture. Given the context of a Kconfig file, it's probably defining a conditional build option, a hardware feature setting, or maybe even a driver configuration related to the IBM mainframe architecture. I'll need to carefully examine the surrounding code to understand the dependencies, default values, and help text associated with this specific configuration item. It could be something straightforward, or it might require a deeper understanding of the hardware capabilities, firmware interactions, or specific kernel subsystems within the s390 environment. Let's see what we can find.



30/2 2026/07/26 08:50 tool read-file
0m Args:
{
  "File": "arch/s390/Kconfig",
  "FirstLine": 830,
  "LineCount": 20
}

Results:
{
  "Contents": " 830:\t\n 831:\t\t  To compile this driver as a module, choose M here: the\n 832:\t\t  module will be called vfio_ccw.\n 833:\t\n 834:\tconfig VFIO_AP\n 835:\t\tdef_tristate n\n 836:\t\tprompt \"VFIO support for AP devices\"\n 837:\t\tdepends on KVM\n 838:\t\tdepends on VFIO\n 839:\t\tdepends on AP\n 840:\t\tselect VFIO_MDEV\n 841:\t\thelp\n 842:\t\t  This driver grants access to Adjunct Processor (AP) devices\n 843:\t\t  via the VFIO mediated device interface.\n 844:\t\n 845:\t\t  To compile this driver as a module, choose M here: the module\n 846:\t\t  will be called vfio_ap.\n 847:\t\n 848:\tendmenu\n 849:\t\n"
}

31/2 2026/07/26 08:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 66547
							output: 16
							thoughts: 56
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Dependencies and Defaults: A Quick Check**

Right, so `CONFIG_VFIO_AP`… I know that one. It's built on a few other pieces. Let me see, it's pretty clear that it depends on `KVM`, `VFIO`, and that `AP` component, which I think refers to `ZCRYPT`, and I'm pretty sure I have that right. The question that pops up, though, is whether the `AP` (or `ZCRYPT`) support is enabled by default. Often these things are set up as modular options. Best thing to do is quickly check the `Kconfig` for the crypto drivers. That should tell me if I need to explicitly enable it or if it's already integrated. I'll jump into `drivers/s390/crypto/Kconfig` and have a look. No need to go any deeper until I know if the base requirement is on.



32/2 2026/07/26 08:51 tool grepper
0m Args:
{
  "Expression": "config AP"
}

Results:
{
  "Output": "Documentation/admin-guide/bootconfig.rst=287=to initrd image, user can notice it before boot.\n--\nDocumentation/admin-guide/bootconfig.rst-289-\nDocumentation/admin-guide/bootconfig.rst:290:Bootconfig APIs\nDocumentation/admin-guide/bootconfig.rst-291-===============\n--\narch/m68k/Kconfig.machine=29=config MAC\n--\narch/m68k/Kconfig.machine-40-\narch/m68k/Kconfig.machine:41:config APOLLO\narch/m68k/Kconfig.machine-42-\tbool \"Apollo support\"\n--\narch/powerpc/platforms/44x/Kconfig=291=config 476FPE\n--\narch/powerpc/platforms/44x/Kconfig-294-\narch/powerpc/platforms/44x/Kconfig:295:config APM821xx\narch/powerpc/platforms/44x/Kconfig-296-\tbool\n--\narch/s390/Kconfig=785=config EADM_SCH\n--\narch/s390/Kconfig-795-\narch/s390/Kconfig:796:config AP\narch/s390/Kconfig-797-\tdef_tristate y\n--\narch/s390/Kconfig-808-\narch/s390/Kconfig:809:config AP_DEBUG\narch/s390/Kconfig-810-\tdef_bool n\n--\narch/s390/Kconfig=885=config CMM_IUCV\n--\narch/s390/Kconfig-892-\narch/s390/Kconfig:893:config APPLDATA_BASE\narch/s390/Kconfig-894-\tdef_bool n\n--\narch/s390/Kconfig-908-\narch/s390/Kconfig:909:config APPLDATA_MEM\narch/s390/Kconfig-910-\tdef_tristate m\n--\narch/s390/Kconfig-925-\narch/s390/Kconfig:926:config APPLDATA_OS\narch/s390/Kconfig-927-\tdef_tristate m\n--\narch/s390/Kconfig-940-\narch/s390/Kconfig:941:config APPLDATA_NET_SUM\narch/s390/Kconfig-942-\tdef_tristate m\n--\narch/x86/Kconfig=2697=config X86_APM_BOOT\n--\narch/x86/Kconfig-2700-\narch/x86/Kconfig:2701:menuconfig APM\narch/x86/Kconfig-2702-\ttristate \"APM (Advanced Power Management) BIOS support\"\n--\narch/x86/Kconfig=2753=if APM\narch/x86/Kconfig-2754-\narch/x86/Kconfig:2755:config APM_IGNORE_USER_SUSPEND\narch/x86/Kconfig-2756-\tbool \"Ignore USER SUSPEND\"\n--\narch/x86/Kconfig-2761-\narch/x86/Kconfig:2762:config APM_DO_ENABLE\narch/x86/Kconfig-2763-\tbool \"Enable PM at boot time\"\n--\narch/x86/Kconfig-2778-\narch/x86/Kconfig:2779:config APM_CPU_IDLE\narch/x86/Kconfig-2780-\tdepends on CPU_IDLE\n--\narch/x86/Kconfig-2790-\narch/x86/Kconfig:2791:config APM_DISPLAY_BLANK\narch/x86/Kconfig-2792-\tbool \"Enable console blanking using APM\"\n--\narch/x86/Kconfig-2803-\narch/x86/Kconfig:2804:config APM_ALLOW_INTS\narch/x86/Kconfig-2805-\tbool \"Allow interrupts during APM BIOS calls\"\n--\ndrivers/clk/qcom/Kconfig=330=config CLK_HAWI_TCSRCC\n--\ndrivers/clk/qcom/Kconfig-336-\ndrivers/clk/qcom/Kconfig:337:config APQ_GCC_8084\ndrivers/clk/qcom/Kconfig-338-\ttristate \"APQ8084 Global Clock Controller\"\n--\ndrivers/clk/qcom/Kconfig-345-\ndrivers/clk/qcom/Kconfig:346:config APQ_MMCC_8084\ndrivers/clk/qcom/Kconfig-347-\ttristate \"APQ8084 Multimedia Clock Controller\"\n--\ndrivers/dma/Kconfig=78=config AMCC_PPC440SPE_ADMA\n--\ndrivers/dma/Kconfig-87-\ndrivers/dma/Kconfig:88:config APPLE_ADMAC\ndrivers/dma/Kconfig-89-\ttristate \"Apple ADMAC support\"\n--\ndrivers/firmware/efi/Kconfig=154=config EFI_DEV_PATH_PARSER\n--\ndrivers/firmware/efi/Kconfig-156-\ndrivers/firmware/efi/Kconfig:157:config APPLE_PROPERTIES\ndrivers/firmware/efi/Kconfig-158-\tbool \"Apple Device Properties\"\n--\ndrivers/iio/light/Kconfig=68=config AL3320A\n--\ndrivers/iio/light/Kconfig-78-\ndrivers/iio/light/Kconfig:79:config APDS9160\ndrivers/iio/light/Kconfig-80-\ttristate \"APDS9160 combined als and proximity sensor\"\n--\ndrivers/iio/light/Kconfig-89-\ndrivers/iio/light/Kconfig:90:config APDS9300\ndrivers/iio/light/Kconfig-91-\ttristate \"APDS9300 ambient light sensor\"\n--\ndrivers/iio/light/Kconfig-99-\ndrivers/iio/light/Kconfig:100:config APDS9306\ndrivers/iio/light/Kconfig-101-\ttristate \"Avago APDS9306 Ambient Light Sensor\"\n--\ndrivers/iio/light/Kconfig-111-\ndrivers/iio/light/Kconfig:112:config APDS9960\ndrivers/iio/light/Kconfig-113-\ttristate \"Avago APDS9960 gesture/RGB/ALS/proximity sensor\"\n--\ndrivers/iio/light/Kconfig-124-\ndrivers/iio/light/Kconfig:125:config APDS9999\ndrivers/iio/light/Kconfig-126-\ttristate \"Broadcom APDS9999 ALS, RGB and proximity sensor\"\n--\ndrivers/iommu/Kconfig=296=config SPAPR_TCE_IOMMU\n--\ndrivers/iommu/Kconfig-303-\ndrivers/iommu/Kconfig:304:config APPLE_DART\ndrivers/iommu/Kconfig-305-\ttristate \"Apple DART IOMMU Support\"\n--\ndrivers/irqchip/Kconfig=805=config IRQ_IDT3243X\n--\ndrivers/irqchip/Kconfig-809-\ndrivers/irqchip/Kconfig:810:config APPLE_AIC\ndrivers/irqchip/Kconfig-811-\tbool \"Apple Interrupt Controller (AIC)\"\n--\ndrivers/misc/Kconfig=327=config SGI_GRU_DEBUG\n--\ndrivers/misc/Kconfig-333-\ndrivers/misc/Kconfig:334:config APDS9802ALS\ndrivers/misc/Kconfig-335-\ttristate \"Medfield Avago APDS9802 ALS Sensor module\"\n--\ndrivers/net/ethernet/8390/Kconfig=105=config NE2K_PCI\n--\ndrivers/net/ethernet/8390/Kconfig-122-\ndrivers/net/ethernet/8390/Kconfig:123:config APNE\ndrivers/net/ethernet/8390/Kconfig-124-\ttristate \"PCMCIA NE2000 support\"\n--\ndrivers/net/wireless/realtek/rtw89/pci.c=3334=static int rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev, bool force)\n--\ndrivers/net/wireless/realtek/rtw89/pci.c-3346-\ndrivers/net/wireless/realtek/rtw89/pci.c:3347:\t/* Configure DAC only via PCI config API, not DBI interfaces */\ndrivers/net/wireless/realtek/rtw89/pci.c-3348-\tret = pci_read_config_byte(pdev, RTW89_PCIE_L1_CTRL, \u0026val);\n--\ndrivers/perf/Kconfig=253=config MARVELL_CN10K_TAD_PMU\n--\ndrivers/perf/Kconfig-259-\ndrivers/perf/Kconfig:260:config APPLE_M1_CPU_PMU\ndrivers/perf/Kconfig-261-\tbool \"Apple M1 CPU PMU support\"\n--\ndrivers/platform/x86/Kconfig=194=config ADV_SWBUTTON\n--\ndrivers/platform/x86/Kconfig-204-\ndrivers/platform/x86/Kconfig:205:config APPLE_GMUX\ndrivers/platform/x86/Kconfig-206-\ttristate \"Apple Gmux Driver\"\n--\ndrivers/pmdomain/apple/Kconfig=3=if ARCH_APPLE || COMPILE_TEST\ndrivers/pmdomain/apple/Kconfig-4-\ndrivers/pmdomain/apple/Kconfig:5:config APPLE_PMGR_PWRSTATE\ndrivers/pmdomain/apple/Kconfig-6-\tbool \"Apple SoC PMGR power state control\"\n--\ndrivers/power/supply/Kconfig=18=config POWER_SUPPLY_HWMON\n--\ndrivers/power/supply/Kconfig-30-\ndrivers/power/supply/Kconfig:31:config APM_POWER\ndrivers/power/supply/Kconfig-32-\ttristate \"APM emulation for class batteries\"\n--\ndrivers/soc/apple/Kconfig=5=menu \"Apple SoC drivers\"\ndrivers/soc/apple/Kconfig-6-\ndrivers/soc/apple/Kconfig:7:config APPLE_MAILBOX\ndrivers/soc/apple/Kconfig-8-\ttristate \"Apple SoC mailboxes\"\n--\ndrivers/soc/apple/Kconfig-18-\ndrivers/soc/apple/Kconfig:19:config APPLE_RTKIT\ndrivers/soc/apple/Kconfig-20-\ttristate \"Apple RTKit co-processor IPC protocol\"\n--\ndrivers/soc/apple/Kconfig-30-\ndrivers/soc/apple/Kconfig:31:config APPLE_SART\ndrivers/soc/apple/Kconfig-32-\ttristate \"Apple SART DMA address filter\"\n--\ndrivers/soc/apple/Kconfig-40-\ndrivers/soc/apple/Kconfig:41:config APPLE_TUNABLE\ndrivers/soc/apple/Kconfig-42-\ttristate\n--\ndrivers/usb/misc/Kconfig=147=config USB_QCOM_EUD\n--\ndrivers/usb/misc/Kconfig-158-\ndrivers/usb/misc/Kconfig:159:config APPLE_MFI_FASTCHARGE\ndrivers/usb/misc/Kconfig-160-\ttristate \"Fast charge control for iOS devices\"\n--\ndrivers/video/Kconfig=6=menu \"Graphics support\"\ndrivers/video/Kconfig-7-\ndrivers/video/Kconfig:8:config APERTURE_HELPERS\ndrivers/video/Kconfig-9-\tbool\n--\ndrivers/watchdog/Kconfig=1111=config MSC313E_WATCHDOG\n--\ndrivers/watchdog/Kconfig-1122-\ndrivers/watchdog/Kconfig:1123:config APPLE_WATCHDOG\ndrivers/watchdog/Kconfig-1124-\ttristate \"Apple SoC watchdog\"\n--\nfs/xfs/xfs_super.c=1655=xfs_fs_fill_super(\n--\nfs/xfs/xfs_super.c-1667-\t * is guaranteed to have been completed by either the old mount API or\nfs/xfs/xfs_super.c:1668:\t * the newer fsopen/fsconfig API.\nfs/xfs/xfs_super.c-1669-\t */\n--\nkernel/power/Kconfig=311=config PM_TRACE_RTC\n--\nkernel/power/Kconfig-328-\nkernel/power/Kconfig:329:config APM_EMULATION\nkernel/power/Kconfig-330-\ttristate \"Advanced Power Management Emulation\"\n"
}

33/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 70185
							output: 26
							thoughts: 320
						

Thoughts:
**My Analysis of VFIO-AP Migration Fuzzing Potential**

Okay, so I'm looking at how we might be able to fuzz the `vfio_ap` migration code. The first thing I see is that `config AP` is indeed defined in `arch/s390/Kconfig` and it's set as `def_tristate y`, so `CONFIG_VFIO_AP` can be enabled. Good, that's the foundation.

Now, the core of the question is, can we actually *reach* and then *fuzz* the code added in `vfio_ap_migration.c`? From what I'm seeing, this patch implements `vfio_ap_set_state`, and that function is triggered by the VFIO core when userspace issues the `VFIO_DEVICE_FEATURE_MIGRATION` ioctl. It looks like this ioctl is available on the mdev device file descriptor.

To get to that point, userspace has to jump through a few hoops. First, they need to create the mdev via sysfs, which looks like it happens through `/sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/create`. Then, they need the VFIO group file descriptor. After that, the mdev needs to be added to the VFIO group. Step four involves binding the VFIO group to a KVM file descriptor, using `VFIO_GROUP_SET_CONTAINER` and `KVM_DEV_VFIO_GROUP_ADD`. That gets you the device file descriptor from the VFIO group, and *then* you can call `VFIO_DEVICE_FEATURE` with `VFIO_DEVICE_FEATURE_MIGRATION`.

This looks like it's within reach. I think this can be fuzzed. It seems like a complicated setup, but it’s pretty straightforward overall.

Now, the question is, does Syzkaller have coverage of this area? Time to check Syzkaller's VFIO descriptions. If it does, and it can already create `vfio_ap` mdevs, then that's a huge step forward.



34/2 2026/07/26 08:51 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "vfio_ap",
  "PathPrefix": "sys/"
}

Results:
{
  "error": "no matches"
}

35/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 71015
							output: 44
							thoughts: 333
						

Thoughts:
**Analyzing Patch Accessibility and Tool Limitations**

Okay, first things first, I need to remember I can't just `grepper` through `sys/`. That's not in the kernel tree, and I need a different approach for syzkaller descriptions. It looks like `read-file` or `codesearch-dir-index` would be the right tools for checking those syzkaller descriptions, but I don't have access to syzkaller descriptions here anyway. Right, the prompt explicitly states I can't search for those with the current tool and to use the mysterious `{{.toolReadDescription}}` tool instead. Hold on… I don't see `toolReadDescription` in my tool list. Strange.

However, the prompt also says that I need to return `WorthFuzzing=false` if the patch concerns code paths that are impossible to reach in virtualized environments, like GCE or QEMU, even with software-emulated hardware. Okay, that's my focus here.

So, the critical question is: Is `vfio_ap` reachable in QEMU? I think so. QEMU supports s390x and can emulate, or at least pass through, VFIO AP. That's a strong indication.

But, before I get carried away, can the kernel create a `vfio_ap` mdev *without* actual physical AP hardware? That's the key. I need to dig into the creation process. Let's start by checking `vfio_ap_mdev_probe` and `vfio_ap_matrix_dev_create`. That `vfio_ap_matrix_dev_create` creates a "matrix" device. I need to find out if that part requires physical AP hardware to succeed. If it *does*, then the patch is unreachable in QEMU and I can mark it as not worth fuzzing.



36/2 2026/07/26 08:51 tool read-file
0m Args:
{
  "File": "drivers/s390/crypto/vfio_ap_drv.c",
  "FirstLine": 88,
  "LineCount": 50
}

Results:
{
  "Contents": "  88:\tstatic int vfio_ap_matrix_dev_create(void)\n  89:\t{\n  90:\t\tint ret;\n  91:\t\tstruct device *root_device;\n  92:\t\n  93:\t\troot_device = root_device_register(VFIO_AP_ROOT_NAME);\n  94:\t\tif (IS_ERR(root_device))\n  95:\t\t\treturn PTR_ERR(root_device);\n  96:\t\n  97:\t\tret = bus_register(\u0026matrix_bus);\n  98:\t\tif (ret)\n  99:\t\t\tgoto bus_register_err;\n 100:\t\n 101:\t\tmatrix_dev = kzalloc_obj(*matrix_dev);\n 102:\t\tif (!matrix_dev) {\n 103:\t\t\tret = -ENOMEM;\n 104:\t\t\tgoto matrix_alloc_err;\n 105:\t\t}\n 106:\t\n 107:\t\t/* Fill in config info via PQAP(QCI), if available */\n 108:\t\tif (test_facility(12)) {\n 109:\t\t\tret = ap_qci(\u0026matrix_dev-\u003einfo);\n 110:\t\t\tif (ret)\n 111:\t\t\t\tgoto matrix_alloc_err;\n 112:\t\t}\n 113:\t\n 114:\t\tmutex_init(\u0026matrix_dev-\u003emdevs_lock);\n 115:\t\tINIT_LIST_HEAD(\u0026matrix_dev-\u003emdev_list);\n 116:\t\tmutex_init(\u0026matrix_dev-\u003eguests_lock);\n 117:\t\n 118:\t\tdev_set_name(\u0026matrix_dev-\u003edevice, \"%s\", VFIO_AP_DEV_NAME);\n 119:\t\tmatrix_dev-\u003edevice.parent = root_device;\n 120:\t\tmatrix_dev-\u003edevice.bus = \u0026matrix_bus;\n 121:\t\tmatrix_dev-\u003edevice.release = vfio_ap_matrix_dev_release;\n 122:\t\tmatrix_dev-\u003evfio_ap_drv = \u0026vfio_ap_drv;\n 123:\t\n 124:\t\tret = device_register(\u0026matrix_dev-\u003edevice);\n 125:\t\tif (ret)\n 126:\t\t\tgoto matrix_reg_err;\n 127:\t\n 128:\t\tret = driver_register(\u0026matrix_driver);\n 129:\t\tif (ret)\n 130:\t\t\tgoto matrix_drv_err;\n 131:\t\n 132:\t\treturn 0;\n 133:\t\n 134:\tmatrix_drv_err:\n 135:\t\tdevice_del(\u0026matrix_dev-\u003edevice);\n 136:\tmatrix_reg_err:\n 137:\t\tput_device(\u0026matrix_dev-\u003edevice);\n"
}

37/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 72525
							output: 45
							thoughts: 145
						

Thoughts:
**My Analysis of the Device Registration Process**

Okay, let me break this down. If `test_facility(12)` returns true, it triggers a call to `ap_qci`, which is expected. But what happens if it's false? Ah, right. It continues straight ahead to registering the device. The key here is what `test_facility(12)` actually checks: the availability of the AP Query Configuration Information facility.

If the facility *isn't* available, and this is important, `matrix_dev->info` gets zeroed out. That makes sense, because it was allocated with `kzalloc_obj`, so it gets initialized to all zeros if the facility isn't present. The device and the driver are registered regardless of facility availability. So, the `matrix` device is created unconditionally, as long as the module successfully loads.

Finally, `vfio_ap_mdev_register` is invoked during `vfio_ap_init`, which probably handles the VFIO-specific setup. This is all coming together; the core device is created and registered regardless of the AP facility presence, and the VFIO aspects are handled separately. I have to see how this impacts my area of expertise to decide if it's correct.



38/2 2026/07/26 08:51 tool read-file
0m Args:
{
  "File": "drivers/s390/crypto/vfio_ap_drv.c",
  "FirstLine": 169,
  "LineCount": 50
}

Results:
{
  "Contents": " 169:\tstatic int __init vfio_ap_init(void)\n 170:\t{\n 171:\t\tint ret;\n 172:\t\n 173:\t\tret = vfio_ap_dbf_info_init();\n 174:\t\tif (ret)\n 175:\t\t\treturn ret;\n 176:\t\n 177:\t\t/* If there are no AP instructions, there is nothing to pass through. */\n 178:\t\tif (!ap_instructions_available())\n 179:\t\t\treturn -ENODEV;\n 180:\t\n 181:\t\tret = vfio_ap_matrix_dev_create();\n 182:\t\tif (ret)\n 183:\t\t\treturn ret;\n 184:\t\n 185:\t\tret = ap_driver_register(\u0026vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME);\n 186:\t\tif (ret) {\n 187:\t\t\tvfio_ap_matrix_dev_destroy();\n 188:\t\t\treturn ret;\n 189:\t\t}\n 190:\t\n 191:\t\tret = vfio_ap_mdev_register();\n 192:\t\tif (ret) {\n 193:\t\t\tap_driver_unregister(\u0026vfio_ap_drv);\n 194:\t\t\tvfio_ap_matrix_dev_destroy();\n 195:\t\n 196:\t\t\treturn ret;\n 197:\t\t}\n 198:\t\n 199:\t\treturn 0;\n 200:\t}\n 201:\t\n 202:\tstatic void __exit vfio_ap_exit(void)\n 203:\t{\n 204:\t\tvfio_ap_mdev_unregister();\n 205:\t\tap_driver_unregister(\u0026vfio_ap_drv);\n 206:\t\tvfio_ap_matrix_dev_destroy();\n 207:\t\tdebug_unregister(vfio_ap_dbf_info);\n 208:\t}\n 209:\t\n 210:\tmodule_init(vfio_ap_init);\n 211:\tmodule_exit(vfio_ap_exit);\n"
}

39/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 73567
							output: 19
							thoughts: 40
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Analyzing the AP Instruction Availability**

Right, so the code's checking `ap_instructions_available()`. That's the key. If this function reports that Advanced Programmable (AP) instructions aren't present – if they aren't accessible for the device – then it returns `-ENODEV`, a common "No such device" error. I need to investigate the implications of this condition. I'll need to drill down and understand how the program is handling the `-ENODEV` return value from `ap_instructions_available()`. Is it gracefully degraded, are alternate paths available, or does it trigger an unrecoverable error? I need to analyze the surrounding code path to determine what behavior the system takes depending on the return of `ap_instructions_available()`. I have to consider the potential for device detection issues that may mask availability and thus must review the calling context. Let me make a note and go check `ap_instructions_available()` right away.



40/2 2026/07/26 08:51 tool grepper
0m Args:
{
  "Expression": "ap_instructions_available"
}

Results:
{
  "Output": "arch/s390/include/asm/ap.h=72=union ap_queue_status_reg {\n--\narch/s390/include/asm/ap.h-80-/**\narch/s390/include/asm/ap.h:81: * ap_instructions_available() - Test if AP instructions are available.\narch/s390/include/asm/ap.h-82- *\n--\narch/s390/include/asm/ap.h-84- */\narch/s390/include/asm/ap.h:85:static inline bool ap_instructions_available(void)\narch/s390/include/asm/ap.h-86-{\n--\narch/s390/kvm/kvm-s390.c=1105=static int kvm_s390_vm_set_crypto(struct kvm *kvm, struct kvm_device_attr *attr)\n--\narch/s390/kvm/kvm-s390.c-1151-\tcase KVM_S390_VM_CRYPTO_ENABLE_APIE:\narch/s390/kvm/kvm-s390.c:1152:\t\tif (!ap_instructions_available()) {\narch/s390/kvm/kvm-s390.c-1153-\t\t\tmutex_unlock(\u0026kvm-\u003elock);\n--\narch/s390/kvm/kvm-s390.c-1158-\tcase KVM_S390_VM_CRYPTO_DISABLE_APIE:\narch/s390/kvm/kvm-s390.c:1159:\t\tif (!ap_instructions_available()) {\narch/s390/kvm/kvm-s390.c-1160-\t\t\tmutex_unlock(\u0026kvm-\u003elock);\n--\narch/s390/kvm/kvm-s390.c=2102=static int kvm_s390_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)\n--\narch/s390/kvm/kvm-s390.c-2158-\t\tcase KVM_S390_VM_CRYPTO_DISABLE_APIE:\narch/s390/kvm/kvm-s390.c:2159:\t\t\tret = ap_instructions_available() ? 0 : -ENXIO;\narch/s390/kvm/kvm-s390.c-2160-\t\t\tbreak;\n--\narch/s390/kvm/kvm-s390.c=3103=static int kvm_s390_apxa_installed(void)\n--\narch/s390/kvm/kvm-s390.c-3106-\narch/s390/kvm/kvm-s390.c:3107:\tif (ap_instructions_available()) {\narch/s390/kvm/kvm-s390.c-3108-\t\tif (ap_qci(\u0026info) == 0)\n--\narch/s390/kvm/priv.c=592=static int handle_pqap(struct kvm_vcpu *vcpu)\n--\narch/s390/kvm/priv.c-600-\t/* Verify that the AP instruction are available */\narch/s390/kvm/priv.c:601:\tif (!ap_instructions_available())\narch/s390/kvm/priv.c-602-\t\treturn -EOPNOTSUPP;\n--\ndrivers/s390/crypto/ap_bus.c=2595=static int __init ap_module_init(void)\n--\ndrivers/s390/crypto/ap_bus.c-2598-\ndrivers/s390/crypto/ap_bus.c:2599:\tif (!ap_instructions_available()) {\ndrivers/s390/crypto/ap_bus.c-2600-\t\tpr_warn(\"The hardware system does not support AP instructions\\n\");\n--\ndrivers/s390/crypto/vfio_ap_drv.c=169=static int __init vfio_ap_init(void)\n--\ndrivers/s390/crypto/vfio_ap_drv.c-177-\t/* If there are no AP instructions, there is nothing to pass through. */\ndrivers/s390/crypto/vfio_ap_drv.c:178:\tif (!ap_instructions_available())\ndrivers/s390/crypto/vfio_ap_drv.c-179-\t\treturn -ENODEV;\n"
}

41/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 75060
							output: 41
							thoughts: 33
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach that query, as my own thought process:

**Investigating the Availability of AP Instructions in s390 Architecture**

Alright, the question of how `ap_instructions_available` works in the s390 architecture is interesting. I'll need to get down into the details. My first instinct, to get a handle on the implementation, is to look directly at the source code. I need to figure out the specific file that will define `ap_instructions_available`. Given that we're dealing with assembly instructions and processor architecture specifics, `arch/s390/include/asm/ap.h` is the most logical place to begin. That header file is likely where the core definition, including any potential inline assembly or function prototypes, will reside. Knowing the s390 architecture as I do, I'd expect it to leverage some clever bit-twiddling or maybe even a specific CPUID-like check to determine instruction availability at runtime. Let's see what the source code reveals.



42/2 2026/07/26 08:51 tool read-file
0m Args:
{
  "File": "arch/s390/include/asm/ap.h",
  "FirstLine": 80,
  "LineCount": 20
}

Results:
{
  "Contents": "  80:\t/**\n  81:\t * ap_instructions_available() - Test if AP instructions are available.\n  82:\t *\n  83:\t * Returns true if the AP instructions are installed, otherwise false.\n  84:\t */\n  85:\tstatic inline bool ap_instructions_available(void)\n  86:\t{\n  87:\t\tunsigned long reg0 = AP_MKQID(0, 0);\n  88:\t\tunsigned long reg1 = 0;\n  89:\t\n  90:\t\tasm volatile(\n  91:\t\t\t\"\tlgr\t0,%[reg0]\\n\"\t\t/* qid into gr0 */\n  92:\t\t\t\"\tlghi\t1,0\\n\"\t\t\t/* 0 into gr1 */\n  93:\t\t\t\"\tlghi\t2,0\\n\"\t\t\t/* 0 into gr2 */\n  94:\t\t\t\"\t.insn\trre,0xb2af0000,0,0\\n\"\t/* PQAP(TAPQ) */\n  95:\t\t\t\"0:\tla\t%[reg1],1\\n\"\t\t/* 1 into reg1 */\n  96:\t\t\t\"1:\\n\"\n  97:\t\t\tEX_TABLE(0b, 1b)\n  98:\t\t\t: [reg1] \"+\u0026d\" (reg1)\n  99:\t\t\t: [reg0] \"d\" (reg0)\n"
}

43/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 75723
							output: 41
							thoughts: 701
						

Thoughts:
**Analyzing the Reachability of AP Crypto Instructions in a Virtualized Environment**

Okay, so I'm diving into this code that's checking for the execution of a `PQAP(TAPQ)` instruction. The first thing I'm doing is trying to figure out if this instruction traps, and if so, what that means in different QEMU configurations. If it traps, the function returns false, which is the key here.

My immediate thought is, "Does `PQAP(TAPQ)` even trap in QEMU?". Then my mind starts racing through the possible configurations. If QEMU is running with KVM, and the host has the AP instructions available, it *might* be accessible. But what if we're using TCG (software emulation)? Does TCG even emulate `PQAP`? I need to check the QEMU source code.

My experience tells me TCG *usually* doesn't emulate AP instructions directly. Then I remember Syzkaller. It often runs s390x in QEMU with TCG, unless it's running on an actual s390x host with KVM. But, Syzkaller *does* have s390x KVM instances, like on IBM Z mainframes. That opens another possibility. But even with KVM, does the host provide AP instructions to the guest by default? It would, if it's got AP cards that are passed through, or intercepted.

Now I remember the prompt's criteria: "Return WorthFuzzing=false if the patch only contains code unreachable in virtualized environments like GCE or QEMU, even with software emulation of hardware and vendor-specific PCIe hardware that's not emulated." This brings up a critical question: Are AP crypto cards, being IBM Z-specific, considered "vendor-specific physical hardware not emulated in standard QEMU"? My instinct is yes. They're not emulated in standard QEMU TCG.

Wait a minute, QEMU *does* have `vfio-ap`, which is a pass-through device. It requires actual AP hardware on the host. Without that host-side hardware, `ap_instructions_available()` will return false, and the `vfio_ap` driver fails to initialize, returning -ENODEV. If `vfio_ap` fails, the mdev can't be created, and the migration code (where `PQAP(TAPQ)` is) can't be reached.

So is there any software emulation of AP instructions in QEMU? No, it's pretty clear that QEMU *only* supports AP instructions via KVM and `vfio-ap` pass-through. I need to verify this assumption.

Okay, let's nail this down. If `ap_instructions_available()` is false, `vfio_ap_init` returns -ENODEV. This means the *entire* `vfio_ap` driver isn't loaded or initialized. This is a crucial point. Can `ap_instructions_available()` return true in a standard QEMU environment without the actual physical AP hardware? The answer is no. `PQAP` is a hardware instruction and will raise an exception unless the hardware supports it (or KVM emulates it).

Does KVM emulate `PQAP` even without the physical hardware? Now, to be absolutely sure I need to check the KVM source code. I need to look at `arch/s390/kvm/priv.c` for `handle_pqap`. And then I'll be confident.



44/2 2026/07/26 08:51 tool read-file
0m Args:
{
  "File": "arch/s390/kvm/priv.c",
  "FirstLine": 592,
  "LineCount": 50
}

Results:
{
  "Contents": " 592:\tstatic int handle_pqap(struct kvm_vcpu *vcpu)\n 593:\t{\n 594:\t\tstruct ap_queue_status status = {};\n 595:\t\tcrypto_hook pqap_hook;\n 596:\t\tunsigned long reg0;\n 597:\t\tint ret;\n 598:\t\tuint8_t fc;\n 599:\t\n 600:\t\t/* Verify that the AP instruction are available */\n 601:\t\tif (!ap_instructions_available())\n 602:\t\t\treturn -EOPNOTSUPP;\n 603:\t\t/* Verify that the guest is allowed to use AP instructions */\n 604:\t\tif (!(vcpu-\u003earch.sie_block-\u003eeca \u0026 ECA_APIE))\n 605:\t\t\treturn -EOPNOTSUPP;\n 606:\t\t/*\n 607:\t\t * The only possibly intercepted functions when AP instructions are\n 608:\t\t * available for the guest are AQIC and TAPQ with the t bit set\n 609:\t\t * since we do not set IC.3 (FIII) we currently will only intercept\n 610:\t\t * the AQIC function code.\n 611:\t\t * Note: running nested under z/VM can result in intercepts for other\n 612:\t\t * function codes, e.g. PQAP(QCI). We do not support this and bail out.\n 613:\t\t */\n 614:\t\treg0 = vcpu-\u003erun-\u003es.regs.gprs[0];\n 615:\t\tfc = (reg0 \u003e\u003e 24) \u0026 0xff;\n 616:\t\tif (fc != 0x03)\n 617:\t\t\treturn -EOPNOTSUPP;\n 618:\t\n 619:\t\t/* PQAP instruction is allowed for guest kernel only */\n 620:\t\tif (vcpu-\u003earch.sie_block-\u003egpsw.mask \u0026 PSW_MASK_PSTATE)\n 621:\t\t\treturn kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);\n 622:\t\n 623:\t\t/* Common PQAP instruction specification exceptions */\n 624:\t\t/* bits 41-47 must all be zeros */\n 625:\t\tif (reg0 \u0026 0x007f0000UL)\n 626:\t\t\treturn kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);\n 627:\t\t/* APFT not install and T bit set */\n 628:\t\tif (!test_kvm_facility(vcpu-\u003ekvm, 15) \u0026\u0026 (reg0 \u0026 0x00800000UL))\n 629:\t\t\treturn kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);\n 630:\t\t/* APXA not installed and APID greater 64 or APQI greater 16 */\n 631:\t\tif (!(vcpu-\u003ekvm-\u003earch.crypto.crycbd \u0026 0x02) \u0026\u0026 (reg0 \u0026 0x0000c0f0UL))\n 632:\t\t\treturn kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);\n 633:\t\n 634:\t\t/* AQIC function code specific exception */\n 635:\t\t/* facility 65 not present for AQIC function code */\n 636:\t\tif (!test_kvm_facility(vcpu-\u003ekvm, 65))\n 637:\t\t\treturn kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);\n 638:\t\n 639:\t\t/*\n 640:\t\t * If the hook callback is registered, there will be a pointer to the\n 641:\t\t * hook function pointer in the kvm_s390_crypto structure. Lock the\n"
}

45/2 2026/07/26 08:51 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 78221
							output: 176
							thoughts: 771
						

Thoughts:
**WorthFuzzing=false: Reasoning for vfio-ap Migration Patch**

Okay, let's break this down. My initial assessment is that this patch, related to live migration for the `vfio-ap` driver, isn't worth fuzzing, and here's why.

First, I need to confirm that my initial understanding is sound. KVM *itself* checks for the availability of AP instructions on the host before even allowing the guest to *attempt* to use them. The `vfio_ap` driver *also* explicitly checks for the hardware using `ap_instructions_available()` during module initialization. If the hardware isn't present, the driver simply won't load, returning `-ENODEV`. This means, and this is crucial, that standard QEMU environments, without dedicated physical IBM Z AP crypto hardware, will *never* reach this code.

And that's the key: is AP crypto hardware considered "vendor-specific physical hardware"? Absolutely. It's IBM Z specific and dedicated cryptographic hardware (Adjunct Processor). And it's *not* emulated by standard QEMU. That neatly falls under the exclusion criteria: code requiring physical PCIe hardware cards not emulated in standard QEMU.

Just to be thorough, I wanted to double-check for any software emulation of AP instructions. A quick search of the kernel source for terms like `ap_bus` and `emulate` turned up nothing of interest. Furthermore, examining `ap_instructions_available()` confirms that it executes a *real* hardware instruction, `PQAP(TAPQ)`. If the hardware doesn't support it, it traps and returns false. So, without that hardware, the driver will not load. Thus, even syzkaller running on standard GCE/QEMU VMs, *even s390x ones*, will not reach this code without specifically provisioning with AP pass-through, which is non-standard.

Let's do a quick review of the patch itself, just to be absolutely certain. The patch modifies `Documentation/arch/s390/vfio-ap.rst`, the `Makefile` in the crypto directory, `vfio_ap_drv.c` to add "migratable" and then adds `vfio_ap_migration.c`, with additions to `vfio_ap_ops.c` related to locking, and `vfio_ap_private.h`. These modifications are entirely within the `vfio-ap` driver. No chance of a misinterpretation there. Since the driver is dependent on the specific physical hardware, it is unreachable in standard environments.

So, the reasoning is this: This patch implements live migration for the s390 vfio_ap (Adjunct Processor) mediated device driver. The vfio_ap driver requires physical IBM Z AP cryptographic hardware to be present on the host system. During module initialization, `vfio_ap_init()` checks `ap_instructions_available()`, which executes a hardware `PQAP` instruction. If the hardware does not support it, the driver returns `-ENODEV` and fails to load. Standard QEMU/KVM environments do not emulate AP cryptographic hardware in software. Therefore, the code paths introduced in this patch are impossible to reach in standard virtualized environments without specific physical hardware, making it unsuitable for fuzzing.

My conclusion is clear: I'll call `set-results` with WorthFuzzing=false.



46/2 2026/07/26 08:51 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch implements live guest migration for the s390 vfio_ap (Adjunct Processor) mediated device driver. The vfio_ap driver requires physical IBM Z AP cryptographic hardware to be present on the host system. During module initialization, `vfio_ap_init()` checks `ap_instructions_available()`, which executes a hardware `PQAP` instruction. If the hardware does not support it, the driver returns `-ENODEV` and fails to load. Standard QEMU/KVM environments do not emulate AP cryptographic hardware in software. Therefore, the code paths introduced in this patch are impossible to reach in standard virtualized environments without specific physical hardware, making it unsuitable for fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch implements live guest migration for the s390 vfio_ap (Adjunct Processor) mediated device driver. The vfio_ap driver requires physical IBM Z AP cryptographic hardware to be present on the host system. During module initialization, `vfio_ap_init()` checks `ap_instructions_available()`, which executes a hardware `PQAP` instruction. If the hardware does not support it, the driver returns `-ENODEV` and fails to load. Standard QEMU/KVM environments do not emulate AP cryptographic hardware in software. Therefore, the code paths introduced in this patch are impossible to reach in standard virtualized environments without specific physical hardware, making it unsuitable for fuzzing.",
  "WorthFuzzing": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)