s390/vfio-ap: allow assignment of unavailable AP queues to mdev device

The current implementation does not allow assignment of an AP adapter or
domain to an mdev device if each APQN resulting from the assignment
does not reference an AP queue device that is bound to the vfio_ap device
driver. This patch allows assignment of AP resources to the matrix mdev as
long as the APQNs resulting from the assignment:
   1. Are not reserved by the AP BUS for use by the zcrypt device drivers.
   2. Are not assigned to another matrix mdev.

The rationale behind this is that the AP architecture does not preclude
assignment of APQNs to an AP configuration profile that are not available
to the system.

Signed-off-by: Tony Krowiak <akrowiak@linux.ibm.com>
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
This commit is contained in:
Tony Krowiak 2020-12-14 21:14:54 -05:00 коммит произвёл Alexander Gordeev
Родитель 48cae940c3
Коммит e2126a7374
1 изменённых файлов: 53 добавлений и 171 удалений

Просмотреть файл

@ -660,141 +660,48 @@ static struct attribute_group *vfio_ap_mdev_type_groups[] = {
NULL, NULL,
}; };
struct vfio_ap_queue_reserved { #define MDEV_SHARING_ERR "Userspace may not re-assign queue %02lx.%04lx " \
unsigned long *apid; "already assigned to %s"
unsigned long *apqi;
bool reserved;
};
/** static void vfio_ap_mdev_log_sharing_err(struct ap_matrix_mdev *matrix_mdev,
* vfio_ap_has_queue - determines if the AP queue containing the target in @data unsigned long *apm,
* unsigned long *aqm)
* @dev: an AP queue device
* @data: a struct vfio_ap_queue_reserved reference
*
* Flags whether the AP queue device (@dev) has a queue ID containing the APQN,
* apid or apqi specified in @data:
*
* - If @data contains both an apid and apqi value, then @data will be flagged
* as reserved if the APID and APQI fields for the AP queue device matches
*
* - If @data contains only an apid value, @data will be flagged as
* reserved if the APID field in the AP queue device matches
*
* - If @data contains only an apqi value, @data will be flagged as
* reserved if the APQI field in the AP queue device matches
*
* Return: 0 to indicate the input to function succeeded. Returns -EINVAL if
* @data does not contain either an apid or apqi.
*/
static int vfio_ap_has_queue(struct device *dev, void *data)
{ {
struct vfio_ap_queue_reserved *qres = data; unsigned long apid, apqi;
struct ap_queue *ap_queue = to_ap_queue(dev); const struct device *dev = mdev_dev(matrix_mdev->mdev);
ap_qid_t qid; const char *mdev_name = dev_name(dev);
unsigned long id;
if (qres->apid && qres->apqi) { for_each_set_bit_inv(apid, apm, AP_DEVICES)
qid = AP_MKQID(*qres->apid, *qres->apqi); for_each_set_bit_inv(apqi, aqm, AP_DOMAINS)
if (qid == ap_queue->qid) dev_warn(dev, MDEV_SHARING_ERR, apid, apqi, mdev_name);
qres->reserved = true;
} else if (qres->apid && !qres->apqi) {
id = AP_QID_CARD(ap_queue->qid);
if (id == *qres->apid)
qres->reserved = true;
} else if (!qres->apid && qres->apqi) {
id = AP_QID_QUEUE(ap_queue->qid);
if (id == *qres->apqi)
qres->reserved = true;
} else {
return -EINVAL;
}
return 0;
} }
/** /**
* vfio_ap_verify_queue_reserved - verifies that the AP queue containing * vfio_ap_mdev_verify_no_sharing - verify APQNs are not shared by matrix mdevs
* @apid or @aqpi is reserved
* *
* @apid: an AP adapter ID * @mdev_apm: mask indicating the APIDs of the APQNs to be verified
* @apqi: an AP queue index * @mdev_aqm: mask indicating the APQIs of the APQNs to be verified
* *
* Verifies that the AP queue with @apid/@apqi is reserved by the VFIO AP device * Verifies that each APQN derived from the Cartesian product of a bitmap of
* driver according to the following rules: * AP adapter IDs and AP queue indexes is not configured for any matrix
*
* - If both @apid and @apqi are not NULL, then there must be an AP queue
* device bound to the vfio_ap driver with the APQN identified by @apid and
* @apqi
*
* - If only @apid is not NULL, then there must be an AP queue device bound
* to the vfio_ap driver with an APQN containing @apid
*
* - If only @apqi is not NULL, then there must be an AP queue device bound
* to the vfio_ap driver with an APQN containing @apqi
*
* Return: 0 if the AP queue is reserved; otherwise, returns -EADDRNOTAVAIL.
*/
static int vfio_ap_verify_queue_reserved(unsigned long *apid,
unsigned long *apqi)
{
int ret;
struct vfio_ap_queue_reserved qres;
qres.apid = apid;
qres.apqi = apqi;
qres.reserved = false;
ret = driver_for_each_device(&matrix_dev->vfio_ap_drv->driver, NULL,
&qres, vfio_ap_has_queue);
if (ret)
return ret;
if (qres.reserved)
return 0;
return -EADDRNOTAVAIL;
}
static int
vfio_ap_mdev_verify_queues_reserved_for_apid(struct ap_matrix_mdev *matrix_mdev,
unsigned long apid)
{
int ret;
unsigned long apqi;
unsigned long nbits = matrix_mdev->matrix.aqm_max + 1;
if (find_first_bit_inv(matrix_mdev->matrix.aqm, nbits) >= nbits)
return vfio_ap_verify_queue_reserved(&apid, NULL);
for_each_set_bit_inv(apqi, matrix_mdev->matrix.aqm, nbits) {
ret = vfio_ap_verify_queue_reserved(&apid, &apqi);
if (ret)
return ret;
}
return 0;
}
/**
* vfio_ap_mdev_verify_no_sharing - verifies that the AP matrix is not configured
*
* @matrix_mdev: the mediated matrix device
*
* 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
* mediated device. AP queue sharing is not allowed. * mediated device. AP queue sharing is not allowed.
* *
* Return: 0 if the APQNs are not shared; otherwise returns -EADDRINUSE. * Return: 0 if the APQNs are not shared; otherwise return -EADDRINUSE.
*/ */
static int vfio_ap_mdev_verify_no_sharing(struct ap_matrix_mdev *matrix_mdev) static int vfio_ap_mdev_verify_no_sharing(unsigned long *mdev_apm,
unsigned long *mdev_aqm)
{ {
struct ap_matrix_mdev *lstdev; struct ap_matrix_mdev *matrix_mdev;
DECLARE_BITMAP(apm, AP_DEVICES); DECLARE_BITMAP(apm, AP_DEVICES);
DECLARE_BITMAP(aqm, AP_DOMAINS); DECLARE_BITMAP(aqm, AP_DOMAINS);
list_for_each_entry(lstdev, &matrix_dev->mdev_list, node) { list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
if (matrix_mdev == lstdev) /*
* If the input apm and aqm are fields of the matrix_mdev
* object, then move on to the next matrix_mdev.
*/
if (mdev_apm == matrix_mdev->matrix.apm &&
mdev_aqm == matrix_mdev->matrix.aqm)
continue; continue;
memset(apm, 0, sizeof(apm)); memset(apm, 0, sizeof(apm));
@ -804,20 +711,32 @@ static int vfio_ap_mdev_verify_no_sharing(struct ap_matrix_mdev *matrix_mdev)
* We work on full longs, as we can only exclude the leftover * We work on full longs, as we can only exclude the leftover
* bits in non-inverse order. The leftover is all zeros. * bits in non-inverse order. The leftover is all zeros.
*/ */
if (!bitmap_and(apm, matrix_mdev->matrix.apm, if (!bitmap_and(apm, mdev_apm, matrix_mdev->matrix.apm,
lstdev->matrix.apm, AP_DEVICES)) AP_DEVICES))
continue; continue;
if (!bitmap_and(aqm, matrix_mdev->matrix.aqm, if (!bitmap_and(aqm, mdev_aqm, matrix_mdev->matrix.aqm,
lstdev->matrix.aqm, AP_DOMAINS)) AP_DOMAINS))
continue; continue;
vfio_ap_mdev_log_sharing_err(matrix_mdev, apm, aqm);
return -EADDRINUSE; return -EADDRINUSE;
} }
return 0; return 0;
} }
static int vfio_ap_mdev_validate_masks(struct ap_matrix_mdev *matrix_mdev)
{
if (ap_apqn_in_matrix_owned_by_def_drv(matrix_mdev->matrix.apm,
matrix_mdev->matrix.aqm))
return -EADDRNOTAVAIL;
return vfio_ap_mdev_verify_no_sharing(matrix_mdev->matrix.apm,
matrix_mdev->matrix.aqm);
}
static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev, static void vfio_ap_mdev_link_adapter(struct ap_matrix_mdev *matrix_mdev,
unsigned long apid) unsigned long apid)
{ {
@ -885,20 +804,13 @@ static ssize_t assign_adapter_store(struct device *dev,
goto done; 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.
*/
ret = vfio_ap_mdev_verify_queues_reserved_for_apid(matrix_mdev, apid);
if (ret)
goto done;
set_bit_inv(apid, matrix_mdev->matrix.apm); set_bit_inv(apid, matrix_mdev->matrix.apm);
ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev); ret = vfio_ap_mdev_validate_masks(matrix_mdev);
if (ret) if (ret) {
goto share_err; clear_bit_inv(apid, matrix_mdev->matrix.apm);
goto done;
}
vfio_ap_mdev_link_adapter(matrix_mdev, apid); vfio_ap_mdev_link_adapter(matrix_mdev, apid);
memset(apm_delta, 0, sizeof(apm_delta)); memset(apm_delta, 0, sizeof(apm_delta));
@ -906,10 +818,6 @@ static ssize_t assign_adapter_store(struct device *dev,
vfio_ap_mdev_filter_matrix(apm_delta, vfio_ap_mdev_filter_matrix(apm_delta,
matrix_mdev->matrix.aqm, matrix_mdev); matrix_mdev->matrix.aqm, matrix_mdev);
ret = count; ret = count;
goto done;
share_err:
clear_bit_inv(apid, matrix_mdev->matrix.apm);
done: done:
mutex_unlock(&matrix_dev->lock); mutex_unlock(&matrix_dev->lock);
@ -984,26 +892,6 @@ done:
} }
static DEVICE_ATTR_WO(unassign_adapter); static DEVICE_ATTR_WO(unassign_adapter);
static int
vfio_ap_mdev_verify_queues_reserved_for_apqi(struct ap_matrix_mdev *matrix_mdev,
unsigned long apqi)
{
int ret;
unsigned long apid;
unsigned long nbits = matrix_mdev->matrix.apm_max + 1;
if (find_first_bit_inv(matrix_mdev->matrix.apm, nbits) >= nbits)
return vfio_ap_verify_queue_reserved(NULL, &apqi);
for_each_set_bit_inv(apid, matrix_mdev->matrix.apm, nbits) {
ret = vfio_ap_verify_queue_reserved(&apid, &apqi);
if (ret)
return ret;
}
return 0;
}
static void vfio_ap_mdev_link_domain(struct ap_matrix_mdev *matrix_mdev, static void vfio_ap_mdev_link_domain(struct ap_matrix_mdev *matrix_mdev,
unsigned long apqi) unsigned long apqi)
{ {
@ -1070,15 +958,13 @@ static ssize_t assign_domain_store(struct device *dev,
goto done; goto done;
} }
ret = vfio_ap_mdev_verify_queues_reserved_for_apqi(matrix_mdev, apqi);
if (ret)
goto done;
set_bit_inv(apqi, matrix_mdev->matrix.aqm); set_bit_inv(apqi, matrix_mdev->matrix.aqm);
ret = vfio_ap_mdev_verify_no_sharing(matrix_mdev); ret = vfio_ap_mdev_validate_masks(matrix_mdev);
if (ret) if (ret) {
goto share_err; clear_bit_inv(apqi, matrix_mdev->matrix.aqm);
goto done;
}
vfio_ap_mdev_link_domain(matrix_mdev, apqi); vfio_ap_mdev_link_domain(matrix_mdev, apqi);
memset(aqm_delta, 0, sizeof(aqm_delta)); memset(aqm_delta, 0, sizeof(aqm_delta));
@ -1086,10 +972,6 @@ static ssize_t assign_domain_store(struct device *dev,
vfio_ap_mdev_filter_matrix(matrix_mdev->matrix.apm, aqm_delta, vfio_ap_mdev_filter_matrix(matrix_mdev->matrix.apm, aqm_delta,
matrix_mdev); matrix_mdev);
ret = count; ret = count;
goto done;
share_err:
clear_bit_inv(apqi, matrix_mdev->matrix.aqm);
done: done:
mutex_unlock(&matrix_dev->lock); mutex_unlock(&matrix_dev->lock);