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