diff mbox series

[v7,14/22] s390: vfio-ap: sysfs interface to activate mdev matrix

Message ID 20180726195429.31960-15-borntraeger@de.ibm.com (mailing list archive)
State New, archived
Headers show
Series vfio-ap: guest dedicated crypto adapters | expand

Commit Message

Christian Borntraeger July 26, 2018, 7:54 p.m. UTC
From: Tony Krowiak <akrowiak@linux.ibm.com>

Provides a sysfs interface to activate AP matrix configured for the
mediated matrix device. To activate the mdev matrix the APQNs that
can be derived from the cross product of adapter and domain IDs
must:

1. Be reserved by the AP bus for use by KVM

2. Not be assigned to another activated matrix mdev

The relevant sysfs structures are:

/sys/devices/vfio_ap
... [matrix]
...... [mdev_supported_types]
......... [vfio_ap-passthrough]
............ [devices]
...............[$uuid]
.................. activate

To activate the matrix configured for the matrix mdev,
write a 1 to the activate file:

echo 1 > activate

To deactivate the matrix configured for the matrix mdev,
write a 0 to the activate file:

echo 0 > activate

To view whether the matrix configured for the mdev
is activated, print the activate file:

cat activate

	0: means not activated
	1: means activated

Signed-off-by: Tony Krowiak <akrowiak@linux.ibm.com>
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Tested-by: Michael Mueller <mimu@linux.ibm.com>
Tested-by: Farhan Ali <alifm@linux.ibm.com>
Tested-by: Pierre Morel <pmorel@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
---
 drivers/s390/crypto/vfio_ap_ops.c     | 244 ++++++++++++++++++++++++--
 drivers/s390/crypto/vfio_ap_private.h |   1 +
 2 files changed, 233 insertions(+), 12 deletions(-)

Comments

Cornelia Huck July 31, 2018, 8:38 a.m. UTC | #1
On Thu, 26 Jul 2018 21:54:21 +0200
Christian Borntraeger <borntraeger@de.ibm.com> wrote:

Regardless whether the activation approach is a good idea...

> +static int vfio_ap_verify_queues_reserved(struct ap_matrix_mdev *matrix_mdev)
> +{
> +	unsigned long apid, apqi;
> +	int ret = 0;
> +
> +	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
> +			     matrix_mdev->matrix.apm_max + 1) {
> +		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
> +				     matrix_mdev->matrix.aqm_max + 1) {
> +			if (!ap_owned_by_def_drv((int)apid, (int)apqi))
> +				continue;
> +
> +			/*
> +			 * We want to log every APQN that is not reserved by
> +			 * the driver, so record the return code, log a message
> +			 * and allow the loop to continue
> +			 */
> +			ret = -EPERM;
> +			pr_warn("%s: activate for %s failed: queue %02lx.%04lx owned by default driver\n",
> +				VFIO_AP_MODULE_NAME, matrix_mdev->name, apid,
> +				apqi);

...I do not think that the syslog is a good place to log those errors.
AFAICS these are setup/administration errors, and the admin may want to
inspect the data to find out what went wrong. Maybe better to log
attempts to assign APQNs reserved to the default drivers into a
dedicated dbf or somesuch, and log it in a format that can also be
digested by scripts?

> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void vfio_ap_mdev_log_sharing_err(struct ap_matrix_mdev *matrix_mdev,
> +					 unsigned long apid, unsigned long apqi)
> +{
> +	pr_warn("%s: AP queue %02lx.%04lx is assigned to %s device\n", __func__,
> +		apid, apqi, matrix_mdev->name);
> +}

Same applies to cross-vm misconfigurations.
Halil Pasic July 31, 2018, 9:14 a.m. UTC | #2
On 07/31/2018 10:38 AM, Cornelia Huck wrote:
> On Thu, 26 Jul 2018 21:54:21 +0200
> Christian Borntraeger <borntraeger@de.ibm.com> wrote:
> 
> Regardless whether the activation approach is a good idea...
> 
>> +static int vfio_ap_verify_queues_reserved(struct ap_matrix_mdev *matrix_mdev)
>> +{
>> +	unsigned long apid, apqi;
>> +	int ret = 0;
>> +
>> +	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
>> +			     matrix_mdev->matrix.apm_max + 1) {
>> +		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
>> +				     matrix_mdev->matrix.aqm_max + 1) {
>> +			if (!ap_owned_by_def_drv((int)apid, (int)apqi))
>> +				continue;
>> +
>> +			/*
>> +			 * We want to log every APQN that is not reserved by
>> +			 * the driver, so record the return code, log a message
>> +			 * and allow the loop to continue
>> +			 */
>> +			ret = -EPERM;
>> +			pr_warn("%s: activate for %s failed: queue %02lx.%04lx owned by default driver\n",
>> +				VFIO_AP_MODULE_NAME, matrix_mdev->name, apid,
>> +				apqi);
> 
> ...I do not think that the syslog is a good place to log those errors.
> AFAICS these are setup/administration errors, and the admin may want to
> inspect the data to find out what went wrong. Maybe better to log
> attempts to assign APQNs reserved to the default drivers into a
> dedicated dbf or somesuch, and log it in a format that can also be
> digested by scripts?
> 

I agree, the error reporting is suboptimal. My preferred solution is an
API with proper error reporting though. Even with dbf the problem, that
one has to read some log and figure out what error corresponds to what
request persist. Optimally the user should get feedback via the same
interface an interactive action was triggered.

In fact, I had a proof of concept implementation but it would need some
rebasing. Another IMHO desirable property of this interface is that it
changes (or rejects change to) the matrix (masks) in one atomic operation.
So there is no 'dirty' state and no transaction involved in specifying
or changing the matrix.

Regards,
Halil

>> +		}
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static void vfio_ap_mdev_log_sharing_err(struct ap_matrix_mdev *matrix_mdev,
>> +					 unsigned long apid, unsigned long apqi)
>> +{
>> +	pr_warn("%s: AP queue %02lx.%04lx is assigned to %s device\n", __func__,
>> +		apid, apqi, matrix_mdev->name);
>> +}
> 
> Same applies to cross-vm misconfigurations.
>
Christian Borntraeger July 31, 2018, 12:19 p.m. UTC | #3
On 07/31/2018 11:14 AM, Halil Pasic wrote:
> 
> 
> On 07/31/2018 10:38 AM, Cornelia Huck wrote:
>> On Thu, 26 Jul 2018 21:54:21 +0200
>> Christian Borntraeger <borntraeger@de.ibm.com> wrote:
>>
>> Regardless whether the activation approach is a good idea...
>>
>>> +static int vfio_ap_verify_queues_reserved(struct ap_matrix_mdev *matrix_mdev)
>>> +{
>>> +    unsigned long apid, apqi;
>>> +    int ret = 0;
>>> +
>>> +    for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
>>> +                 matrix_mdev->matrix.apm_max + 1) {
>>> +        for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
>>> +                     matrix_mdev->matrix.aqm_max + 1) {
>>> +            if (!ap_owned_by_def_drv((int)apid, (int)apqi))
>>> +                continue;
>>> +
>>> +            /*
>>> +             * We want to log every APQN that is not reserved by
>>> +             * the driver, so record the return code, log a message
>>> +             * and allow the loop to continue
>>> +             */
>>> +            ret = -EPERM;
>>> +            pr_warn("%s: activate for %s failed: queue %02lx.%04lx owned by default driver\n",
>>> +                VFIO_AP_MODULE_NAME, matrix_mdev->name, apid,
>>> +                apqi);
>>
>> ...I do not think that the syslog is a good place to log those errors.
>> AFAICS these are setup/administration errors, and the admin may want to
>> inspect the data to find out what went wrong. Maybe better to log
>> attempts to assign APQNs reserved to the default drivers into a
>> dedicated dbf or somesuch, and log it in a format that can also be
>> digested by scripts?
>>
> 
> I agree, the error reporting is suboptimal. My preferred solution is an
> API with proper error reporting though. Even with dbf the problem, that
> one has to read some log and figure out what error corresponds to what
> request persist. Optimally the user should get feedback via the same
> interface an interactive action was triggered.
> 
> In fact, I had a proof of concept implementation but it would need some
> rebasing. Another IMHO desirable property of this interface is that it
> changes (or rejects change to) the matrix (masks) in one atomic operation.
> So there is no 'dirty' state and no transaction involved in specifying
> or changing the matrix.

lets just drop the printks for the time being. Its the normal way of kernel
things to return with -EINVAL and NOT telling why.
Cornelia Huck Aug. 2, 2018, 11:21 a.m. UTC | #4
On Tue, 31 Jul 2018 14:19:23 +0200
Christian Borntraeger <borntraeger@de.ibm.com> wrote:

> lets just drop the printks for the time being. Its the normal way of kernel
> things to return with -EINVAL and NOT telling why.

Agreed. Some non-printk logging can still be added at a later time
should we want that.
Anthony Krowiak Aug. 2, 2018, 9:10 p.m. UTC | #5
On 08/02/2018 07:21 AM, Cornelia Huck wrote:
> On Tue, 31 Jul 2018 14:19:23 +0200
> Christian Borntraeger <borntraeger@de.ibm.com> wrote:
>
>> lets just drop the printks for the time being. Its the normal way of kernel
>> things to return with -EINVAL and NOT telling why.
> Agreed. Some non-printk logging can still be added at a later time
> should we want that.

Will do.

>
diff mbox series

Patch

diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c
index 9258902d9f70..675aa97612f6 100644
--- a/drivers/s390/crypto/vfio_ap_ops.c
+++ b/drivers/s390/crypto/vfio_ap_ops.c
@@ -52,6 +52,12 @@  static int vfio_ap_mdev_remove(struct mdev_device *mdev)
 	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 
 	mutex_lock(&matrix_dev.lock);
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: mdev remove failed: mdev %s is activated",
+			VFIO_AP_MODULE_NAME, __func__, matrix_mdev->name);
+		mutex_unlock(&matrix_dev.lock);
+		return -EBUSY;
+	}
 	list_del(&matrix_mdev->list);
 	mutex_unlock(&matrix_dev.lock);
 	kfree(matrix_mdev);
@@ -125,6 +131,15 @@  static ssize_t assign_adapter_store(struct device *dev,
 	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 	unsigned long max_apid = matrix_mdev->matrix.apm_max;
 
+	mutex_lock(&matrix_dev.lock);
+
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: assign adapter '%s' failed: mdev %s is activated\n",
+			VFIO_AP_MODULE_NAME, __func__, buf, matrix_mdev->name);
+		ret = -EBUSY;
+		goto done;
+	}
+
 	ret = kstrtoul(buf, 0, &apid);
 	if (ret || (apid > max_apid)) {
 		pr_warn("%s: %s: adapter id '%s' not a value from 0 to %02lu(%#04lx)\n",
@@ -133,17 +148,17 @@  static ssize_t assign_adapter_store(struct device *dev,
 		if (!ret)
 			ret = -EINVAL;
 
-		return ret;
+		goto done;
 	}
 
 	/* Set the bit in the AP mask (APM) corresponding to the AP adapter
 	 * number (APID). The bits in the mask, from most significant to least
 	 * significant bit, correspond to APIDs 0-255.
 	 */
-	mutex_lock(&matrix_dev.lock);
 	set_bit_inv(apid, matrix_mdev->matrix.apm);
 	ret = count;
 
+done:
 	mutex_unlock(&matrix_dev.lock);
 
 	return ret;
@@ -175,6 +190,15 @@  static ssize_t unassign_adapter_store(struct device *dev,
 
 	unsigned long max_apid = matrix_mdev->matrix.apm_max;
 
+	mutex_lock(&matrix_dev.lock);
+
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: unassign adapter '%s' failed: mdev %s is activated\n",
+			VFIO_AP_MODULE_NAME, __func__, buf, matrix_mdev->name);
+		ret = -EBUSY;
+		goto done;
+	}
+
 	ret = kstrtoul(buf, 0, &apid);
 	if (ret || (apid > max_apid)) {
 		pr_warn("%s: %s: adapter id '%s' must be a value from 0 to %02lu(%#04lx)\n",
@@ -183,10 +207,9 @@  static ssize_t unassign_adapter_store(struct device *dev,
 		if (!ret)
 			ret = -EINVAL;
 
-		return ret;
+		goto done;
 	}
 
-	mutex_lock(&matrix_dev.lock);
 	if (!test_bit_inv(apid, matrix_mdev->matrix.apm)) {
 		pr_warn("%s: %s: adapter id %02lu(%#04lx) not assigned\n",
 		       VFIO_AP_MODULE_NAME, __func__, apid, apid);
@@ -214,6 +237,15 @@  static ssize_t assign_domain_store(struct device *dev,
 	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 	unsigned long max_apqi = matrix_mdev->matrix.aqm_max;
 
+	mutex_lock(&matrix_dev.lock);
+
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: assign domain '%s' failed: mdev %s is activated\n",
+			VFIO_AP_MODULE_NAME, __func__, buf, matrix_mdev->name);
+		ret = -EBUSY;
+		goto done;
+	}
+
 	ret = kstrtoul(buf, 0, &apqi);
 	if (ret || (apqi > max_apqi)) {
 		pr_warn("%s: %s: domain id '%s' not a value from 0 to %02lu(%#04lx)\n",
@@ -222,17 +254,17 @@  static ssize_t assign_domain_store(struct device *dev,
 		if (!ret)
 			ret = -EINVAL;
 
-		return ret;
+		goto done;
 	}
 
 	/* Set the bit in the AQM (bitmask) corresponding to the AP domain
 	 * number (APQI). The bits in the mask, from most significant to least
 	 * significant, correspond to numbers 0-255.
 	 */
-	mutex_lock(&matrix_dev.lock);
 	set_bit_inv(apqi, matrix_mdev->matrix.aqm);
 	ret = count;
 
+done:
 	mutex_unlock(&matrix_dev.lock);
 
 	return ret;
@@ -249,15 +281,23 @@  static ssize_t unassign_domain_store(struct device *dev,
 	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 	unsigned long max_apqi = matrix_mdev->matrix.aqm_max;
 
+	mutex_lock(&matrix_dev.lock);
+
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: unassign domain '%s' failed: mdev %s is activated\n",
+			VFIO_AP_MODULE_NAME, __func__, buf, matrix_mdev->name);
+		ret = -EBUSY;
+		goto done;
+	}
+
 	ret = kstrtoul(buf, 0, &apqi);
 	if (ret || (apqi > max_apqi)) {
 		pr_warn("%s: %s: domain id '%s' not a value from 0 to %02lu(%#04lx)\n",
 		       VFIO_AP_MODULE_NAME, __func__, buf, max_apqi, max_apqi);
 		ret = -EINVAL;
-		return ret;
+		goto done;
 	}
 
-	mutex_lock(&matrix_dev.lock);
 	if (!test_bit_inv(apqi, matrix_mdev->matrix.aqm)) {
 		pr_warn("%s: %s: domain %02lu(%#04lx) not assigned\n",
 		       VFIO_AP_MODULE_NAME, __func__, apqi, apqi);
@@ -301,6 +341,15 @@  static ssize_t assign_control_domain_store(struct device *dev,
 	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 	unsigned long maxid = matrix_mdev->matrix.adm_max;
 
+	mutex_lock(&matrix_dev.lock);
+
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: assign control domain '%s' failed: mdev %s is activated\n",
+			VFIO_AP_MODULE_NAME, __func__, buf, matrix_mdev->name);
+		ret = -EBUSY;
+		goto done;
+	}
+
 	ret = kstrtoul(buf, 0, &id);
 	if (ret || (id > maxid)) {
 		pr_warn("%s: %s: control domain id '%s' not a value from 0 to %02lu(%#04lx)\n",
@@ -309,7 +358,7 @@  static ssize_t assign_control_domain_store(struct device *dev,
 		if (!ret)
 			ret = -EINVAL;
 
-		return ret;
+		goto done;
 	}
 
 	/* Set the bit in the ADM (bitmask) corresponding to the AP control
@@ -317,10 +366,10 @@  static ssize_t assign_control_domain_store(struct device *dev,
 	 * least significant, correspond to IDs 0 up to the one less than the
 	 * number of control domains that can be assigned.
 	 */
-	mutex_lock(&matrix_dev.lock);
 	set_bit_inv(id, matrix_mdev->matrix.adm);
 	ret = count;
 
+done:
 	mutex_unlock(&matrix_dev.lock);
 
 	return ret;
@@ -351,16 +400,24 @@  static ssize_t unassign_control_domain_store(struct device *dev,
 	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
 	unsigned long max_domid =  matrix_mdev->matrix.adm_max;
 
+	mutex_lock(&matrix_dev.lock);
+
+	if (matrix_mdev->activated) {
+		pr_warn("%s: %s: unassign control domain '%s' failed: mdev %s is activated\n",
+			VFIO_AP_MODULE_NAME, __func__, buf, matrix_mdev->name);
+		ret = -EBUSY;
+		goto done;
+	}
+
 	ret = kstrtoul(buf, 0, &domid);
 	if (ret || (domid > max_domid)) {
 		pr_warn("%s: %s: control domain id '%s' not a value from 0 to %02lu(%#04lx)\n",
 		       VFIO_AP_MODULE_NAME, __func__, buf,
 		       max_domid, max_domid);
 		ret = -EINVAL;
-		return ret;
+		goto done;
 	}
 
-	mutex_lock(&matrix_dev.lock);
 	if (!test_bit_inv(domid, matrix_mdev->matrix.adm)) {
 		pr_warn("%s: %s: control domain id %02lu(%#04lx) is not assigned\n",
 		       VFIO_AP_MODULE_NAME, __func__, domid, domid);
@@ -434,6 +491,168 @@  static ssize_t matrix_show(struct device *dev, struct device_attribute *attr,
 }
 DEVICE_ATTR_RO(matrix);
 
+static int vfio_ap_verify_queues_reserved(struct ap_matrix_mdev *matrix_mdev)
+{
+	unsigned long apid, apqi;
+	int ret = 0;
+
+	for_each_set_bit_inv(apid, matrix_mdev->matrix.apm,
+			     matrix_mdev->matrix.apm_max + 1) {
+		for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm,
+				     matrix_mdev->matrix.aqm_max + 1) {
+			if (!ap_owned_by_def_drv((int)apid, (int)apqi))
+				continue;
+
+			/*
+			 * We want to log every APQN that is not reserved by
+			 * the driver, so record the return code, log a message
+			 * and allow the loop to continue
+			 */
+			ret = -EPERM;
+			pr_warn("%s: activate for %s failed: queue %02lx.%04lx owned by default driver\n",
+				VFIO_AP_MODULE_NAME, matrix_mdev->name, apid,
+				apqi);
+		}
+	}
+
+	return ret;
+}
+
+static void vfio_ap_mdev_log_sharing_err(struct ap_matrix_mdev *matrix_mdev,
+					 unsigned long apid, unsigned long apqi)
+{
+	pr_warn("%s: AP queue %02lx.%04lx is assigned to %s device\n", __func__,
+		apid, apqi, matrix_mdev->name);
+}
+
+/**
+ * vfio_ap_mdev_verify_no_sharing
+ *
+ * Verifies that the APQNs derived from the cross product of the AP adapter IDs
+ * and AP queue indexes comprising the AP matrix are not configured for another
+ * activated mediated device. AP queue sharing is not allowed.
+ *
+ * @kvm: the KVM guest
+ * @matrix: the AP matrix
+ *
+ * Returns 0 if the APQNs are valid, otherwise; returns -EBUSY.
+ */
+static int vfio_ap_mdev_verify_no_sharing(struct ap_matrix_mdev *matrix_mdev)
+{
+	int nbits;
+	struct ap_matrix_mdev *lstdev;
+	unsigned long apid, apqi;
+	unsigned long apm[BITS_TO_LONGS(matrix_mdev->matrix.apm_max + 1)];
+	unsigned long aqm[BITS_TO_LONGS(matrix_mdev->matrix.aqm_max + 1)];
+
+	list_for_each_entry(lstdev, &matrix_dev.mdev_list, list) {
+		if ((matrix_mdev == lstdev) || !lstdev->activated)
+			continue;
+
+		memset(apm, 0, sizeof(apm));
+		memset(aqm, 0, sizeof(aqm));
+
+		/*
+		 * We work on full longs, as we can only exclude the leftover
+		 * bits in non-inverse order. The leftover is all zeros.
+		 */
+		nbits = sizeof(apm) * BITS_PER_BYTE;
+		if (!bitmap_and(apm, matrix_mdev->matrix.apm,
+				lstdev->matrix.apm, nbits))
+			continue;
+
+		nbits = sizeof(aqm) * BITS_PER_BYTE;
+		if (!bitmap_and(aqm, matrix_mdev->matrix.aqm,
+				lstdev->matrix.aqm, nbits))
+			continue;
+
+		goto sharing_err;
+	}
+	return 0;
+
+sharing_err:
+
+	for_each_set_bit_inv(apid, apm, matrix_mdev->matrix.apm_max + 1)
+		for_each_set_bit_inv(apqi, aqm,
+				     matrix_mdev->matrix.aqm_max + 1)
+			vfio_ap_mdev_log_sharing_err(lstdev, apid, apqi);
+
+	return -EPERM;
+}
+
+static int vfio_ap_mdev_activate(struct ap_matrix_mdev *matrix_mdev)
+{
+	int ret = 0;
+
+	if (matrix_mdev->activated)
+		return 0;
+
+	ret = vfio_ap_verify_queues_reserved(matrix_mdev);
+	if (ret)
+		return ret;
+
+	ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev);
+	if (ret)
+		return ret;
+
+	matrix_mdev->activated = 1;
+
+	return 0;
+}
+
+static int vfio_ap_mdev_deactivate(struct ap_matrix_mdev *matrix_mdev)
+{
+	if (!matrix_mdev->activated)
+		return 0;
+
+	matrix_mdev->activated = false;
+
+	return 0;
+}
+
+static ssize_t activate_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	int ret;
+	unsigned int activate;
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	ret = kstrtouint(buf, 0, &activate);
+	if (ret || ((activate != 0) && (activate != 1))) {
+		pr_warn("%s: %s: input to activate '%s' not 0 or 1\n",
+			VFIO_AP_MODULE_NAME, __func__, buf);
+
+		if (!ret)
+			ret = -EINVAL;
+
+		return ret;
+	}
+
+	mutex_lock(&matrix_dev.lock);
+
+	ret = (activate) ? vfio_ap_mdev_activate(matrix_mdev) :
+			   vfio_ap_mdev_deactivate(matrix_mdev);
+	if (ret)
+		goto done;
+
+	ret = count;
+
+done:
+	mutex_unlock(&matrix_dev.lock);
+
+	return ret;
+}
+
+static ssize_t activate_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct mdev_device *mdev = mdev_from_dev(dev);
+	struct ap_matrix_mdev *matrix_mdev = mdev_get_drvdata(mdev);
+
+	return sprintf(buf, "%d\n", matrix_mdev->activated);
+}
+DEVICE_ATTR_RW(activate);
 
 static struct attribute *vfio_ap_mdev_attrs[] = {
 	&dev_attr_assign_adapter.attr,
@@ -444,6 +663,7 @@  static struct attribute *vfio_ap_mdev_attrs[] = {
 	&dev_attr_unassign_control_domain.attr,
 	&dev_attr_control_domains.attr,
 	&dev_attr_matrix.attr,
+	&dev_attr_activate.attr,
 	NULL,
 };
 
diff --git a/drivers/s390/crypto/vfio_ap_private.h b/drivers/s390/crypto/vfio_ap_private.h
index c2fd4a3e0ae3..df1996e6fce3 100644
--- a/drivers/s390/crypto/vfio_ap_private.h
+++ b/drivers/s390/crypto/vfio_ap_private.h
@@ -63,6 +63,7 @@  struct ap_matrix_mdev {
 	const char *name;
 	struct list_head list;
 	struct ap_matrix matrix;
+	bool activated;
 };
 
 static inline struct device *to_device(struct ap_matrix_dev *matrix_dev)