s390/vfio-ap: reset queues filtered from the guest's AP config
[platform/kernel/linux-starfive.git] / drivers / s390 / crypto / vfio_ap_ops.c
index 29ac0b9..82a3511 100644 (file)
@@ -32,7 +32,8 @@
 
 #define AP_RESET_INTERVAL              20      /* Reset sleep interval (20ms)          */
 
-static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable);
+static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev);
+static int vfio_ap_mdev_reset_qlist(struct list_head *qlist);
 static struct vfio_ap_queue *vfio_ap_find_queue(int apqn);
 static const struct vfio_device_ops vfio_ap_matrix_dev_ops;
 static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q);
@@ -662,16 +663,23 @@ static bool vfio_ap_mdev_filter_cdoms(struct ap_matrix_mdev *matrix_mdev)
  *                             device driver.
  *
  * @matrix_mdev: the matrix mdev whose matrix is to be filtered.
+ * @apm_filtered: a 256-bit bitmap for storing the APIDs filtered from the
+ *               guest's AP configuration that are still in the host's AP
+ *               configuration.
  *
  * Note: If an APQN referencing a queue device that is not bound to the vfio_ap
  *      driver, its APID will be filtered from the guest's APCB. The matrix
  *      structure precludes filtering an individual APQN, so its APID will be
- *      filtered.
+ *      filtered. Consequently, all queues associated with the adapter that
+ *      are in the host's AP configuration must be reset. If queues are
+ *      subsequently made available again to the guest, they should re-appear
+ *      in a reset state
  *
  * Return: a boolean value indicating whether the KVM guest's APCB was changed
  *        by the filtering or not.
  */
-static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev)
+static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev,
+                                      unsigned long *apm_filtered)
 {
        unsigned long apid, apqi, apqn;
        DECLARE_BITMAP(prev_shadow_apm, AP_DEVICES);
@@ -681,6 +689,7 @@ static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev)
        bitmap_copy(prev_shadow_apm, matrix_mdev->shadow_apcb.apm, AP_DEVICES);
        bitmap_copy(prev_shadow_aqm, matrix_mdev->shadow_apcb.aqm, AP_DOMAINS);
        vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb);
+       bitmap_clear(apm_filtered, 0, AP_DEVICES);
 
        /*
         * Copy the adapters, domains and control domains to the shadow_apcb
@@ -706,8 +715,16 @@ static bool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev)
                        apqn = AP_MKQID(apid, apqi);
                        q = vfio_ap_mdev_get_queue(matrix_mdev, apqn);
                        if (!q || q->reset_status.response_code) {
-                               clear_bit_inv(apid,
-                                             matrix_mdev->shadow_apcb.apm);
+                               clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm);
+
+                               /*
+                                * If the adapter was previously plugged into
+                                * the guest, let's let the caller know that
+                                * the APID was filtered.
+                                */
+                               if (test_bit_inv(apid, prev_shadow_apm))
+                                       set_bit_inv(apid, apm_filtered);
+
                                break;
                        }
                }
@@ -809,7 +826,7 @@ static void vfio_ap_mdev_remove(struct mdev_device *mdev)
 
        mutex_lock(&matrix_dev->guests_lock);
        mutex_lock(&matrix_dev->mdevs_lock);
-       vfio_ap_mdev_reset_queues(&matrix_mdev->qtable);
+       vfio_ap_mdev_reset_queues(matrix_mdev);
        vfio_ap_mdev_unlink_fr_queues(matrix_mdev);
        list_del(&matrix_mdev->node);
        mutex_unlock(&matrix_dev->mdevs_lock);
@@ -919,6 +936,47 @@ static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev,
                                       AP_MKQID(apid, apqi));
 }
 
+static int reset_queues_for_apids(struct ap_matrix_mdev *matrix_mdev,
+                                 unsigned long *apm_reset)
+{
+       struct vfio_ap_queue *q, *tmpq;
+       struct list_head qlist;
+       unsigned long apid, apqi;
+       int apqn, ret = 0;
+
+       if (bitmap_empty(apm_reset, AP_DEVICES))
+               return 0;
+
+       INIT_LIST_HEAD(&qlist);
+
+       for_each_set_bit_inv(apid, apm_reset, AP_DEVICES) {
+               for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm,
+                                    AP_DOMAINS) {
+                       /*
+                        * If the domain is not in the host's AP configuration,
+                        * then resetting it will fail with response code 01
+                        * (APQN not valid).
+                        */
+                       if (!test_bit_inv(apqi,
+                                         (unsigned long *)matrix_dev->info.aqm))
+                               continue;
+
+                       apqn = AP_MKQID(apid, apqi);
+                       q = vfio_ap_mdev_get_queue(matrix_mdev, apqn);
+
+                       if (q)
+                               list_add_tail(&q->reset_qnode, &qlist);
+               }
+       }
+
+       ret = vfio_ap_mdev_reset_qlist(&qlist);
+
+       list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode)
+               list_del(&q->reset_qnode);
+
+       return ret;
+}
+
 /**
  * assign_adapter_store - parses the APID from @buf and sets the
  * corresponding bit in the mediated matrix device's APM
@@ -959,6 +1017,7 @@ static ssize_t assign_adapter_store(struct device *dev,
 {
        int ret;
        unsigned long apid;
+       DECLARE_BITMAP(apm_filtered, AP_DEVICES);
        struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
 
        mutex_lock(&ap_perms_mutex);
@@ -988,8 +1047,10 @@ static ssize_t assign_adapter_store(struct device *dev,
 
        vfio_ap_mdev_link_adapter(matrix_mdev, apid);
 
-       if (vfio_ap_mdev_filter_matrix(matrix_mdev))
+       if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) {
                vfio_ap_mdev_update_guest_apcb(matrix_mdev);
+               reset_queues_for_apids(matrix_mdev, apm_filtered);
+       }
 
        ret = count;
 done:
@@ -1020,11 +1081,12 @@ static struct vfio_ap_queue
  *                              adapter was assigned.
  * @matrix_mdev: the matrix mediated device to which the adapter was assigned.
  * @apid: the APID of the unassigned adapter.
- * @qtable: table for storing queues associated with unassigned adapter.
+ * @qlist: list for storing queues associated with unassigned adapter that
+ *        need to be reset.
  */
 static void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev,
                                        unsigned long apid,
-                                       struct ap_queue_table *qtable)
+                                       struct list_head *qlist)
 {
        unsigned long apqi;
        struct vfio_ap_queue *q;
@@ -1032,11 +1094,10 @@ static void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev,
        for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, AP_DOMAINS) {
                q = vfio_ap_unlink_apqn_fr_mdev(matrix_mdev, apid, apqi);
 
-               if (q && qtable) {
+               if (q && qlist) {
                        if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) &&
                            test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm))
-                               hash_add(qtable->queues, &q->mdev_qnode,
-                                        q->apqn);
+                               list_add_tail(&q->reset_qnode, qlist);
                }
        }
 }
@@ -1044,26 +1105,23 @@ static void vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev,
 static void vfio_ap_mdev_hot_unplug_adapter(struct ap_matrix_mdev *matrix_mdev,
                                            unsigned long apid)
 {
-       int loop_cursor;
-       struct vfio_ap_queue *q;
-       struct ap_queue_table *qtable = kzalloc(sizeof(*qtable), GFP_KERNEL);
+       struct vfio_ap_queue *q, *tmpq;
+       struct list_head qlist;
 
-       hash_init(qtable->queues);
-       vfio_ap_mdev_unlink_adapter(matrix_mdev, apid, qtable);
+       INIT_LIST_HEAD(&qlist);
+       vfio_ap_mdev_unlink_adapter(matrix_mdev, apid, &qlist);
 
        if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm)) {
                clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm);
                vfio_ap_mdev_update_guest_apcb(matrix_mdev);
        }
 
-       vfio_ap_mdev_reset_queues(qtable);
+       vfio_ap_mdev_reset_qlist(&qlist);
 
-       hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) {
+       list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) {
                vfio_ap_unlink_mdev_fr_queue(q);
-               hash_del(&q->mdev_qnode);
+               list_del(&q->reset_qnode);
        }
-
-       kfree(qtable);
 }
 
 /**
@@ -1164,6 +1222,7 @@ static ssize_t assign_domain_store(struct device *dev,
 {
        int ret;
        unsigned long apqi;
+       DECLARE_BITMAP(apm_filtered, AP_DEVICES);
        struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
 
        mutex_lock(&ap_perms_mutex);
@@ -1193,8 +1252,10 @@ static ssize_t assign_domain_store(struct device *dev,
 
        vfio_ap_mdev_link_domain(matrix_mdev, apqi);
 
-       if (vfio_ap_mdev_filter_matrix(matrix_mdev))
+       if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) {
                vfio_ap_mdev_update_guest_apcb(matrix_mdev);
+               reset_queues_for_apids(matrix_mdev, apm_filtered);
+       }
 
        ret = count;
 done:
@@ -1207,7 +1268,7 @@ static DEVICE_ATTR_WO(assign_domain);
 
 static void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev,
                                       unsigned long apqi,
-                                      struct ap_queue_table *qtable)
+                                      struct list_head *qlist)
 {
        unsigned long apid;
        struct vfio_ap_queue *q;
@@ -1215,11 +1276,10 @@ static void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev,
        for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, AP_DEVICES) {
                q = vfio_ap_unlink_apqn_fr_mdev(matrix_mdev, apid, apqi);
 
-               if (q && qtable) {
+               if (q && qlist) {
                        if (test_bit_inv(apid, matrix_mdev->shadow_apcb.apm) &&
                            test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm))
-                               hash_add(qtable->queues, &q->mdev_qnode,
-                                        q->apqn);
+                               list_add_tail(&q->reset_qnode, qlist);
                }
        }
 }
@@ -1227,26 +1287,23 @@ static void vfio_ap_mdev_unlink_domain(struct ap_matrix_mdev *matrix_mdev,
 static void vfio_ap_mdev_hot_unplug_domain(struct ap_matrix_mdev *matrix_mdev,
                                           unsigned long apqi)
 {
-       int loop_cursor;
-       struct vfio_ap_queue *q;
-       struct ap_queue_table *qtable = kzalloc(sizeof(*qtable), GFP_KERNEL);
+       struct vfio_ap_queue *q, *tmpq;
+       struct list_head qlist;
 
-       hash_init(qtable->queues);
-       vfio_ap_mdev_unlink_domain(matrix_mdev, apqi, qtable);
+       INIT_LIST_HEAD(&qlist);
+       vfio_ap_mdev_unlink_domain(matrix_mdev, apqi, &qlist);
 
        if (test_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm)) {
                clear_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm);
                vfio_ap_mdev_update_guest_apcb(matrix_mdev);
        }
 
-       vfio_ap_mdev_reset_queues(qtable);
+       vfio_ap_mdev_reset_qlist(&qlist);
 
-       hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) {
+       list_for_each_entry_safe(q, tmpq, &qlist, reset_qnode) {
                vfio_ap_unlink_mdev_fr_queue(q);
-               hash_del(&q->mdev_qnode);
+               list_del(&q->reset_qnode);
        }
-
-       kfree(qtable);
 }
 
 /**
@@ -1601,7 +1658,7 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
                get_update_locks_for_kvm(kvm);
 
                kvm_arch_crypto_clear_masks(kvm);
-               vfio_ap_mdev_reset_queues(&matrix_mdev->qtable);
+               vfio_ap_mdev_reset_queues(matrix_mdev);
                kvm_put_kvm(kvm);
                matrix_mdev->kvm = NULL;
 
@@ -1737,15 +1794,33 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q)
        }
 }
 
-static int vfio_ap_mdev_reset_queues(struct ap_queue_table *qtable)
+static int vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev)
 {
        int ret = 0, loop_cursor;
        struct vfio_ap_queue *q;
 
-       hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode)
+       hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode)
                vfio_ap_mdev_reset_queue(q);
 
-       hash_for_each(qtable->queues, loop_cursor, q, mdev_qnode) {
+       hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode) {
+               flush_work(&q->reset_work);
+
+               if (q->reset_status.response_code)
+                       ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int vfio_ap_mdev_reset_qlist(struct list_head *qlist)
+{
+       int ret = 0;
+       struct vfio_ap_queue *q;
+
+       list_for_each_entry(q, qlist, reset_qnode)
+               vfio_ap_mdev_reset_queue(q);
+
+       list_for_each_entry(q, qlist, reset_qnode) {
                flush_work(&q->reset_work);
 
                if (q->reset_status.response_code)
@@ -1931,7 +2006,7 @@ static ssize_t vfio_ap_mdev_ioctl(struct vfio_device *vdev,
                ret = vfio_ap_mdev_get_device_info(arg);
                break;
        case VFIO_DEVICE_RESET:
-               ret = vfio_ap_mdev_reset_queues(&matrix_mdev->qtable);
+               ret = vfio_ap_mdev_reset_queues(matrix_mdev);
                break;
        case VFIO_DEVICE_GET_IRQ_INFO:
                        ret = vfio_ap_get_irq_info(arg);
@@ -2063,6 +2138,7 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
 {
        int ret;
        struct vfio_ap_queue *q;
+       DECLARE_BITMAP(apm_filtered, AP_DEVICES);
        struct ap_matrix_mdev *matrix_mdev;
 
        ret = sysfs_create_group(&apdev->device.kobj, &vfio_queue_attr_group);
@@ -2095,15 +2171,17 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
                    !bitmap_empty(matrix_mdev->aqm_add, AP_DOMAINS))
                        goto done;
 
-               if (vfio_ap_mdev_filter_matrix(matrix_mdev))
+               if (vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered)) {
                        vfio_ap_mdev_update_guest_apcb(matrix_mdev);
+                       reset_queues_for_apids(matrix_mdev, apm_filtered);
+               }
        }
 
 done:
        dev_set_drvdata(&apdev->device, q);
        release_update_locks_for_mdev(matrix_mdev);
 
-       return 0;
+       return ret;
 
 err_remove_group:
        sysfs_remove_group(&apdev->device.kobj, &vfio_queue_attr_group);
@@ -2447,6 +2525,7 @@ void vfio_ap_on_cfg_changed(struct ap_config_info *cur_cfg_info,
 
 static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
 {
+       DECLARE_BITMAP(apm_filtered, AP_DEVICES);
        bool filter_domains, filter_adapters, filter_cdoms, do_hotplug = false;
 
        mutex_lock(&matrix_mdev->kvm->lock);
@@ -2460,7 +2539,7 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
                                         matrix_mdev->adm_add, AP_DOMAINS);
 
        if (filter_adapters || filter_domains)
-               do_hotplug = vfio_ap_mdev_filter_matrix(matrix_mdev);
+               do_hotplug = vfio_ap_mdev_filter_matrix(matrix_mdev, apm_filtered);
 
        if (filter_cdoms)
                do_hotplug |= vfio_ap_mdev_filter_cdoms(matrix_mdev);
@@ -2468,6 +2547,8 @@ static void vfio_ap_mdev_hot_plug_cfg(struct ap_matrix_mdev *matrix_mdev)
        if (do_hotplug)
                vfio_ap_mdev_update_guest_apcb(matrix_mdev);
 
+       reset_queues_for_apids(matrix_mdev, apm_filtered);
+
        mutex_unlock(&matrix_dev->mdevs_lock);
        mutex_unlock(&matrix_mdev->kvm->lock);
 }