diff mbox series

[v6,06/10] s390: vfio-ap: update guest CRYCB in vfio_ap probe and remove callbacks

Message ID 1568410018-10833-7-git-send-email-akrowiak@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series s390: vfio-ap: dynamic configuration support | expand

Commit Message

Anthony Krowiak Sept. 13, 2019, 9:26 p.m. UTC
When the vfio_ap device driver's probe callback is invoked to bind a
an AP queue device, if its APQN has been assigned to a matrix mdev that is
in use by a guest, the queue will be hot plugged into the guest's AP
configuration if:

1. Each APQN derived from the APID and the APQIs already set in the
   guest's CRYCB references an AP queue device bound to the vfio_ap
   device driver.

2. Each APQN derived from the APQI and the APIDs already set in the
   guest's CRYCB references an AP queue device bound to the vfio_ap
   device driver.

When an AP adapter is removed from the AP configuration via the SE or an
'SCLP Deconfigure AP Adapter' instruction, each queue device associated
with the adapter will be unbound from device driver to which it is bound.
When the vfio_ap device driver's remove callback is invoked to unbind an
AP queue device, if the queue's APQN is assigned to a matrix mdev in use
by a guest, the adapter to which the queue is connected will be hot
unplugged from the guest's configuration. The reason for this is because
an adapter of a different type can be added back to the AP configuration
with the APID corresponding to the adapter being removed. If the new
adapter is not of the appropriate type, it will not get bound to the
vfio_ap driver. If we did not unplug it from the guest, both the guest
and the host would have access to all of the queues associated with the
adapter which is a no-no.

Signed-off-by: Tony Krowiak <akrowiak@linux.ibm.com>
---
 drivers/s390/crypto/vfio_ap_ops.c | 93 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)
diff mbox series

Patch

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 14f221b7426b..f3332424670f 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -507,6 +507,63 @@  static void vfio_ap_mdev_get_crycb_matrix(struct ap_matrix_mdev *matrix_mdev)
 	}
 }
 
+static bool vfio_ap_mdev_update_crycb_matrix(struct ap_matrix_mdev *matrix_mdev,
+					     struct vfio_ap_queue *q)
+{
+	unsigned long crycb_apid, crycb_apqi;
+	unsigned long apid = AP_QID_CARD(q->apqn);
+	unsigned long apqi = AP_QID_QUEUE(q->apqn);
+
+	/*
+	 * If the APID of the input queue is not already set in the guest's
+	 * CRYCB, then verify that all APQNs derived from Cartesian product of
+	 * the APID and each APQI set in the guest's CRYCB references a queue
+	 * that is bound to the vfio_ap driver.
+	 */
+	if (!test_bit_inv(apid, matrix_mdev->crycb.apm)) {
+		for_each_set_bit_inv(crycb_apqi, matrix_mdev->crycb.aqm,
+				     matrix_mdev->crycb.aqm_max + 1) {
+			/*
+			 * The APQN formulated from the APID and APQI of the
+			 * input queue is in the process of being bound to the
+			 * vfio_ap driver so there is no need to verify it.
+			 */
+			if (apqi == crycb_apqi)
+				continue;
+
+			if (!vfio_ap_find_queue(AP_MKQID(apid, crycb_apqi)))
+				return false;
+		}
+	}
+
+	/*
+	 * If the APQI of the input queue is not already set in the guest's
+	 * CRYCB, then verify that all APQNs derived from Cartesian product of
+	 * the APQI and each APID set in the guest's CRYCB references a queue
+	 * that is bound to the vfio_ap driver.
+	 */
+	if (!test_bit_inv(apqi, matrix_mdev->crycb.aqm)) {
+		for_each_set_bit_inv(crycb_apid, matrix_mdev->crycb.apm,
+				     matrix_mdev->crycb.apm_max + 1) {
+			/*
+			 * The APQN formulated from the APID and APQI of the
+			 * input queue is in the process of being bound to the
+			 * vfio_ap driver so there is no need to verify it.
+			 */
+			if (apid == crycb_apid)
+				continue;
+
+			if (!vfio_ap_find_queue(AP_MKQID(crycb_apid, apqi)))
+				return false;
+		}
+	}
+
+	set_bit_inv(apid, matrix_mdev->crycb.apm);
+	set_bit_inv(apqi, matrix_mdev->crycb.aqm);
+
+	return true;
+}
+
 static bool vfio_ap_mdev_has_crycb(struct ap_matrix_mdev *matrix_mdev)
 {
 	return matrix_mdev->kvm && matrix_mdev->kvm->arch.crypto.crycbd;
@@ -1311,9 +1368,23 @@  void vfio_ap_mdev_unregister(void)
 	mdev_unregister_device(&matrix_dev->device);
 }
 
+static struct ap_matrix_mdev *vfio_ap_mdev_for_queue(int apqn)
+{
+	struct ap_matrix_mdev *matrix_mdev;
+
+	list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
+		if (test_bit_inv(AP_QID_CARD(apqn), matrix_mdev->matrix.apm) &&
+		    test_bit_inv(AP_QID_QUEUE(apqn), matrix_mdev->matrix.aqm))
+			return matrix_mdev;
+	}
+
+	return NULL;
+}
+
 int vfio_ap_mdev_probe_queue(struct ap_queue *queue)
 {
 	struct vfio_ap_queue *q;
+	struct ap_matrix_mdev *matrix_mdev;
 
 	q = kzalloc(sizeof(*q), GFP_KERNEL);
 	if (!q)
@@ -1322,18 +1393,40 @@  int vfio_ap_mdev_probe_queue(struct ap_queue *queue)
 	q->apqn = queue->qid;
 	q->saved_isc = VFIO_AP_ISC_INVALID;
 
+	/*
+	 * If the APQN for the queue is assigned to a matrix mdev that is in
+	 * use by a guest, then plug the queue into the guest.
+	 */
+	matrix_mdev = vfio_ap_mdev_for_queue(queue->qid);
+	if (matrix_mdev && vfio_ap_mdev_has_crycb(matrix_mdev)) {
+		vfio_ap_mdev_update_crycb_matrix(matrix_mdev, q);
+		vfio_ap_mdev_update_crycb(matrix_mdev);
+	}
+
 	return 0;
 }
 
 void vfio_ap_mdev_remove_queue(struct ap_queue *queue)
 {
 	struct vfio_ap_queue *q;
+	struct ap_matrix_mdev *matrix_mdev;
 	int apid, apqi;
 
 	q = dev_get_drvdata(&queue->ap_dev.device);
 	dev_set_drvdata(&queue->ap_dev.device, NULL);
 	apid = AP_QID_CARD(q->apqn);
 	apqi = AP_QID_QUEUE(q->apqn);
+
+	/*
+	 * If the APQN for the queue is assigned to a matrix mdev that is in
+	 * use by a guest, then unplug the adapter from the guest.
+	 */
+	matrix_mdev = vfio_ap_mdev_for_queue(queue->qid);
+	if (matrix_mdev && vfio_ap_mdev_has_crycb(matrix_mdev)) {
+		clear_bit_inv(apid, matrix_mdev->crycb.apm);
+		vfio_ap_mdev_update_crycb(matrix_mdev);
+	}
+
 	vfio_ap_mdev_reset_queue(apid, apqi);
 	vfio_ap_irq_disable(q);
 	kfree(q);