@@ -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,
};
@@ -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)