/** * get_update_locks_for_kvm: Acquire the locks required to dynamically update a * KVM guest's APCB in the proper order. * * @kvm: a pointer to a struct kvm object containing the KVM guest's APCB. * * The proper locking order is: * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM * guest's APCB. * 2. kvm->lock: required to update a guest's APCB * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev * * Note: If @kvm is NULL, the KVM lock will not be taken.
*/ staticinlinevoid get_update_locks_for_kvm(struct kvm *kvm)
{
mutex_lock(&matrix_dev->guests_lock); if (kvm)
mutex_lock(&kvm->lock);
mutex_lock(&matrix_dev->mdevs_lock);
}
/** * release_update_locks_for_kvm: Release the locks used to dynamically update a * KVM guest's APCB in the proper order. * * @kvm: a pointer to a struct kvm object containing the KVM guest's APCB. * * The proper unlocking order is: * 1. matrix_dev->mdevs_lock * 2. kvm->lock * 3. matrix_dev->guests_lock * * Note: If @kvm is NULL, the KVM lock will not be released.
*/ staticinlinevoid release_update_locks_for_kvm(struct kvm *kvm)
{
mutex_unlock(&matrix_dev->mdevs_lock); if (kvm)
mutex_unlock(&kvm->lock);
mutex_unlock(&matrix_dev->guests_lock);
}
/** * get_update_locks_for_mdev: Acquire the locks required to dynamically update a * KVM guest's APCB in the proper order. * * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP * configuration data to use to update a KVM guest's APCB. * * The proper locking order is: * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM * guest's APCB. * 2. matrix_mdev->kvm->lock: required to update a guest's APCB * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev * * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM * lock will not be taken.
*/ staticinlinevoid get_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
{
mutex_lock(&matrix_dev->guests_lock); if (matrix_mdev && matrix_mdev->kvm)
mutex_lock(&matrix_mdev->kvm->lock);
mutex_lock(&matrix_dev->mdevs_lock);
}
/** * release_update_locks_for_mdev: Release the locks used to dynamically update a * KVM guest's APCB in the proper order. * * @matrix_mdev: a pointer to a struct ap_matrix_mdev object containing the AP * configuration data to use to update a KVM guest's APCB. * * The proper unlocking order is: * 1. matrix_dev->mdevs_lock * 2. matrix_mdev->kvm->lock * 3. matrix_dev->guests_lock * * Note: If @matrix_mdev is NULL or is not attached to a KVM guest, the KVM * lock will not be released.
*/ staticinlinevoid release_update_locks_for_mdev(struct ap_matrix_mdev *matrix_mdev)
{
mutex_unlock(&matrix_dev->mdevs_lock); if (matrix_mdev && matrix_mdev->kvm)
mutex_unlock(&matrix_mdev->kvm->lock);
mutex_unlock(&matrix_dev->guests_lock);
}
/** * get_update_locks_by_apqn: Find the mdev to which an APQN is assigned and * acquire the locks required to update the APCB of * the KVM guest to which the mdev is attached. * * @apqn: the APQN of a queue device. * * The proper locking order is: * 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM * guest's APCB. * 2. matrix_mdev->kvm->lock: required to update a guest's APCB * 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev * * Note: If @apqn is not assigned to a matrix_mdev, the matrix_mdev->kvm->lock * will not be taken. * * Return: the ap_matrix_mdev object to which @apqn is assigned or NULL if @apqn * is not assigned to an ap_matrix_mdev.
*/ staticstruct ap_matrix_mdev *get_update_locks_by_apqn(int apqn)
{ struct ap_matrix_mdev *matrix_mdev;
mutex_lock(&matrix_dev->guests_lock);
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)) { if (matrix_mdev->kvm)
mutex_lock(&matrix_mdev->kvm->lock);
mutex_lock(&matrix_dev->mdevs_lock);
return matrix_mdev;
}
}
mutex_lock(&matrix_dev->mdevs_lock);
return NULL;
}
/** * get_update_locks_for_queue: get the locks required to update the APCB of the * KVM guest to which the matrix mdev linked to a * vfio_ap_queue object is attached. * * @q: a pointer to a vfio_ap_queue object. * * The proper locking order is: * 1. q->matrix_dev->guests_lock: required to use the KVM pointer to update a * KVM guest's APCB. * 2. q->matrix_mdev->kvm->lock: required to update a guest's APCB * 3. matrix_dev->mdevs_lock: required to access data stored in matrix_mdev * * Note: if @queue is not linked to an ap_matrix_mdev object, the KVM lock * will not be taken.
*/ staticinlinevoid get_update_locks_for_queue(struct vfio_ap_queue *q)
{
mutex_lock(&matrix_dev->guests_lock); if (q->matrix_mdev && q->matrix_mdev->kvm)
mutex_lock(&q->matrix_mdev->kvm->lock);
mutex_lock(&matrix_dev->mdevs_lock);
}
/** * vfio_ap_mdev_get_queue - retrieve a queue with a specific APQN from a * hash table of queues assigned to a matrix mdev * @matrix_mdev: the matrix mdev * @apqn: The APQN of a queue device * * Return: the pointer to the vfio_ap_queue struct representing the queue or * NULL if the queue is not assigned to @matrix_mdev
*/ staticstruct vfio_ap_queue *vfio_ap_mdev_get_queue( struct ap_matrix_mdev *matrix_mdev, int apqn)
{ struct vfio_ap_queue *q;
hash_for_each_possible(matrix_mdev->qtable.queues, q, mdev_qnode,
apqn) { if (q && q->apqn == apqn) return q;
}
return NULL;
}
/** * vfio_ap_wait_for_irqclear - clears the IR bit or gives up after 5 tries * @apqn: The AP Queue number * * Checks the IRQ bit for the status of this APQN using ap_tapq. * Returns if the ap_tapq function succeeded and the bit is clear. * Returns if ap_tapq function failed with invalid, deconfigured or * checkstopped AP. * Otherwise retries up to 5 times after waiting 20ms.
*/ staticvoid vfio_ap_wait_for_irqclear(int apqn)
{ struct ap_queue_status status; int retry = 5;
do {
status = ap_tapq(apqn, NULL); switch (status.response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_RESET_IN_PROGRESS: if (!status.irq_enabled) return;
fallthrough; case AP_RESPONSE_BUSY:
msleep(20); break; case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: default:
WARN_ONCE(1, "%s: tapq rc %02x: %04x\n", __func__,
status.response_code, apqn); return;
}
} while (--retry);
WARN_ONCE(1, "%s: tapq rc %02x: %04x could not clear IR bit\n",
__func__, status.response_code, apqn);
}
/** * vfio_ap_free_aqic_resources - free vfio_ap_queue resources * @q: The vfio_ap_queue * * Unregisters the ISC in the GIB when the saved ISC not invalid. * Unpins the guest's page holding the NIB when it exists. * Resets the saved_iova and saved_isc to invalid values.
*/ staticvoid vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)
{ if (!q) return; if (q->saved_isc != VFIO_AP_ISC_INVALID &&
!WARN_ON(!(q->matrix_mdev && q->matrix_mdev->kvm))) {
kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
q->saved_isc = VFIO_AP_ISC_INVALID;
} if (q->saved_iova && !WARN_ON(!q->matrix_mdev)) {
vfio_unpin_pages(&q->matrix_mdev->vdev, q->saved_iova, 1);
q->saved_iova = 0;
}
}
/** * vfio_ap_irq_disable - disables and clears an ap_queue interrupt * @q: The vfio_ap_queue * * Uses ap_aqic to disable the interruption and in case of success, reset * in progress or IRQ disable command already proceeded: calls * vfio_ap_wait_for_irqclear() to check for the IRQ bit to be clear * and calls vfio_ap_free_aqic_resources() to free the resources associated * with the AP interrupt handling. * * In the case the AP is busy, or a reset is in progress, * retries after 20ms, up to 5 times. * * Returns if ap_aqic function failed with invalid, deconfigured or * checkstopped AP. * * Return: &struct ap_queue_status
*/ staticstruct ap_queue_status vfio_ap_irq_disable(struct vfio_ap_queue *q)
{ union ap_qirq_ctrl aqic_gisa = { .value = 0 }; struct ap_queue_status status; int retries = 5;
do {
status = ap_aqic(q->apqn, aqic_gisa, 0); switch (status.response_code) { case AP_RESPONSE_OTHERWISE_CHANGED: case AP_RESPONSE_NORMAL:
vfio_ap_wait_for_irqclear(q->apqn); goto end_free; case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_BUSY:
msleep(20); break; case AP_RESPONSE_Q_NOT_AVAIL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: case AP_RESPONSE_INVALID_ADDRESS: default: /* All cases in default means AP not operational */
WARN_ONCE(1, "%s: ap_aqic status %d\n", __func__,
status.response_code); goto end_free;
}
} while (retries--);
/** * vfio_ap_validate_nib - validate a notification indicator byte (nib) address. * * @vcpu: the object representing the vcpu executing the PQAP(AQIC) instruction. * @nib: the location for storing the nib address. * * When the PQAP(AQIC) instruction is executed, general register 2 contains the * address of the notification indicator byte (nib) used for IRQ notification. * This function parses and validates the nib from gr2. * * Return: returns zero if the nib address is a valid; otherwise, returns * -EINVAL.
*/ staticint vfio_ap_validate_nib(struct kvm_vcpu *vcpu, dma_addr_t *nib)
{
*nib = vcpu->run->s.regs.gprs[2];
if (!*nib) return -EINVAL; if (kvm_is_error_hva(gfn_to_hva(vcpu->kvm, *nib >> PAGE_SHIFT))) return -EINVAL;
return 0;
}
/** * ensure_nib_shared() - Ensure the address of the NIB is secure and shared * @addr: the physical (absolute) address of the NIB * * This function checks whether the NIB page, which has been pinned with * vfio_pin_pages(), is a shared page belonging to a secure guest. * * It will call uv_pin_shared() on it; if the page was already pinned shared * (i.e. if the NIB belongs to a secure guest and is shared), then 0 * (success) is returned. If the NIB was not shared, vfio_pin_pages() had * exported it and now it does not belong to the secure guest anymore. In * that case, an error is returned. * * Context: the NIB (at physical address @addr) has to be pinned with * vfio_pin_pages() before calling this function. * * Return: 0 in case of success, otherwise an error < 0.
*/ staticint ensure_nib_shared(unsignedlong addr)
{ /* * The nib has to be located in shared storage since guest and * host access it. vfio_pin_pages() will do a pin shared and * if that fails (possibly because it's not a shared page) it * calls export. We try to do a second pin shared here so that * the UV gives us an error code if we try to pin a non-shared * page. * * If the page is already pinned shared the UV will return a success.
*/ return uv_pin_shared(addr);
}
/** * vfio_ap_irq_enable - Enable Interruption for a APQN * * @q: the vfio_ap_queue holding AQIC parameters * @isc: the guest ISC to register with the GIB interface * @vcpu: the vcpu object containing the registers specifying the parameters * passed to the PQAP(AQIC) instruction. * * Pin the NIB saved in *q * Register the guest ISC to GIB interface and retrieve the * host ISC to issue the host side PQAP/AQIC * * status.response_code may be set to AP_RESPONSE_INVALID_ADDRESS in case the * vfio_pin_pages or kvm_s390_gisc_register failed. * * Otherwise return the ap_queue_status returned by the ap_aqic(), * all retry handling will be done by the guest. * * Return: &struct ap_queue_status
*/ staticstruct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q, int isc, struct kvm_vcpu *vcpu)
{ union ap_qirq_ctrl aqic_gisa = { .value = 0 }; struct ap_queue_status status = {}; struct kvm_s390_gisa *gisa; struct page *h_page; int nisc; struct kvm *kvm;
phys_addr_t h_nib;
dma_addr_t nib; int ret;
/* Verify that the notification indicator byte address is valid */ if (vfio_ap_validate_nib(vcpu, &nib)) {
VFIO_AP_DBF_WARN("%s: invalid NIB address: nib=%pad, apqn=%#04x\n",
__func__, &nib, q->apqn);
/* The pin will probably be successful even if the NIB was not shared */
ret = vfio_pin_pages(&q->matrix_mdev->vdev, nib, 1,
IOMMU_READ | IOMMU_WRITE, &h_page); switch (ret) { case 1: break; default:
VFIO_AP_DBF_WARN("%s: vfio_pin_pages failed: rc=%d," "nib=%pad, apqn=%#04x\n",
__func__, ret, &nib, q->apqn);
/** * vfio_ap_le_guid_to_be_uuid - convert a little endian guid array into an array * of big endian elements that can be passed by * value to an s390dbf sprintf event function to * format a UUID string. * * @guid: the object containing the little endian guid * @uuid: a six-element array of long values that can be passed by value as * arguments for a formatting string specifying a UUID. * * The S390 Debug Feature (s390dbf) allows the use of "%s" in the sprintf * event functions if the memory for the passed string is available as long as * the debug feature exists. Since a mediated device can be removed at any * time, it's name can not be used because %s passes the reference to the string * in memory and the reference will go stale once the device is removed . * * The s390dbf string formatting function allows a maximum of 9 arguments for a * message to be displayed in the 'sprintf' view. In order to use the bytes * comprising the mediated device's UUID to display the mediated device name, * they will have to be converted into an array whose elements can be passed by * value to sprintf. For example: * * guid array: { 83, 78, 17, 62, bb, f1, f0, 47, 91, 4d, 32, a2, 2e, 3a, 88, 04 } * mdev name: 62177883-f1bb-47f0-914d-32a22e3a8804 * array returned: { 62177883, f1bb, 47f0, 914d, 32a2, 2e3a8804 } * formatting string: "%08lx-%04lx-%04lx-%04lx-%02lx%04lx"
*/ staticvoid vfio_ap_le_guid_to_be_uuid(guid_t *guid, unsignedlong *uuid)
{ /* * The input guid is ordered in little endian, so it needs to be * reordered for displaying a UUID as a string. This specifies the * guid indices in proper order.
*/
uuid[0] = le32_to_cpup((__le32 *)guid);
uuid[1] = le16_to_cpup((__le16 *)&guid->b[4]);
uuid[2] = le16_to_cpup((__le16 *)&guid->b[6]);
uuid[3] = *((__u16 *)&guid->b[8]);
uuid[4] = *((__u16 *)&guid->b[10]);
uuid[5] = *((__u32 *)&guid->b[12]);
}
/** * handle_pqap - PQAP instruction callback * * @vcpu: The vcpu on which we received the PQAP instruction * * Get the general register contents to initialize internal variables. * REG[0]: APQN * REG[1]: IR and ISC * REG[2]: NIB * * Response.status may be set to following Response Code: * - AP_RESPONSE_Q_NOT_AVAIL: if the queue is not available * - AP_RESPONSE_DECONFIGURED: if the queue is not configured * - AP_RESPONSE_NORMAL (0) : in case of success * Check vfio_ap_setirq() and vfio_ap_clrirq() for other possible RC. * We take the matrix_dev lock to ensure serialization on queues and * mediated device access. * * Return: 0 if we could handle the request inside KVM. * Otherwise, returns -EOPNOTSUPP to let QEMU handle the fault.
*/ staticint handle_pqap(struct kvm_vcpu *vcpu)
{
uint64_t status;
uint16_t apqn; unsignedlong uuid[6]; struct vfio_ap_queue *q; struct ap_queue_status qstatus = {
.response_code = AP_RESPONSE_Q_NOT_AVAIL, }; struct ap_matrix_mdev *matrix_mdev;
apqn = vcpu->run->s.regs.gprs[0] & 0xffff;
/* If we do not use the AIV facility just go to userland */ if (!(vcpu->arch.sie_block->eca & ECA_AIV)) {
VFIO_AP_DBF_WARN("%s: AIV facility not installed: apqn=0x%04x, eca=0x%04x\n",
__func__, apqn, vcpu->arch.sie_block->eca);
return -EOPNOTSUPP;
}
mutex_lock(&matrix_dev->mdevs_lock);
if (!vcpu->kvm->arch.crypto.pqap_hook) {
VFIO_AP_DBF_WARN("%s: PQAP(AQIC) hook not registered with the vfio_ap driver: apqn=0x%04x\n",
__func__, apqn);
/* If the there is no guest using the mdev, there is nothing to do */ if (!matrix_mdev->kvm) {
vfio_ap_le_guid_to_be_uuid(&matrix_mdev->mdev->uuid, uuid);
VFIO_AP_DBF_WARN("%s: mdev %08lx-%04lx-%04lx-%04lx-%04lx%08lx not in use: apqn=0x%04x\n",
__func__, uuid[0], uuid[1], uuid[2],
uuid[3], uuid[4], uuid[5], apqn); goto out_unlock;
}
q = vfio_ap_mdev_get_queue(matrix_mdev, apqn); if (!q) {
VFIO_AP_DBF_WARN("%s: Queue %02x.%04x not bound to the vfio_ap driver\n",
__func__, AP_QID_CARD(apqn),
AP_QID_QUEUE(apqn)); goto out_unlock;
}
status = vcpu->run->s.regs.gprs[1];
/* If IR bit(16) is set we enable the interrupt */ if ((status >> (63 - 16)) & 0x01)
qstatus = vfio_ap_irq_enable(q, status & 0x07, vcpu); else
qstatus = vfio_ap_irq_disable(q);
staticbool _queue_passable(struct vfio_ap_queue *q)
{ if (!q) returnfalse;
switch (q->reset_status.response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: returntrue; default: returnfalse;
}
}
/* * vfio_ap_mdev_filter_matrix - filter the APQNs assigned to the matrix mdev * to ensure no queue devices are passed through to * the guest that are not bound to the vfio_ap * device driver. * * @matrix_mdev: the matrix mdev whose matrix is to be filtered. * @apm_filtered: a 256-bit bitmap for storing the APIDs filtered from the * guest's AP configuration that are still in the host's AP * configuration. * * Note: If an APQN referencing a queue device that is not bound to the vfio_ap * driver, its APID will be filtered from the guest's APCB. The matrix * structure precludes filtering an individual APQN, so its APID will be * filtered. Consequently, all queues associated with the adapter that * are in the host's AP configuration must be reset. If queues are * subsequently made available again to the guest, they should re-appear * in a reset state * * Return: a boolean value indicating whether the KVM guest's APCB was changed * by the filtering or not.
*/ staticbool vfio_ap_mdev_filter_matrix(struct ap_matrix_mdev *matrix_mdev, unsignedlong *apm_filtered)
{ unsignedlong apid, apqi, apqn;
DECLARE_BITMAP(prev_shadow_apm, AP_DEVICES);
DECLARE_BITMAP(prev_shadow_aqm, AP_DOMAINS);
/* * Copy the adapters, domains and control domains to the shadow_apcb * from the matrix mdev, but only those that are assigned to the host's * AP configuration.
*/
bitmap_and(matrix_mdev->shadow_apcb.apm, matrix_mdev->matrix.apm,
(unsignedlong *)matrix_dev->info.apm, AP_DEVICES);
bitmap_and(matrix_mdev->shadow_apcb.aqm, matrix_mdev->matrix.aqm,
(unsignedlong *)matrix_dev->info.aqm, AP_DOMAINS);
for_each_set_bit_inv(apid, matrix_mdev->shadow_apcb.apm, AP_DEVICES) {
for_each_set_bit_inv(apqi, matrix_mdev->shadow_apcb.aqm,
AP_DOMAINS) { /* * If the APQN is not bound to the vfio_ap device * driver, then we can't assign it to the guest's * AP configuration. The AP architecture won't * allow filtering of a single APQN, so let's filter * the APID since an adapter represents a physical * hardware device.
*/
apqn = AP_MKQID(apid, apqi); if (!_queue_passable(vfio_ap_mdev_get_queue(matrix_mdev, apqn))) {
clear_bit_inv(apid, matrix_mdev->shadow_apcb.apm);
/* * If the adapter was previously plugged into * the guest, let's let the caller know that * the APID was filtered.
*/ if (test_bit_inv(apid, prev_shadow_apm))
set_bit_inv(apid, apm_filtered);
/** * vfio_ap_mdev_verify_no_sharing - verify APQNs are not shared by matrix mdevs * * @assignee: the matrix mdev to which @mdev_apm and @mdev_aqm are being * assigned; or, NULL if this function was called by the AP bus * driver in_use callback to verify none of the APQNs being reserved * for the host device driver are in use by a vfio_ap mediated device * @mdev_apm: mask indicating the APIDs of the APQNs to be verified * @mdev_aqm: mask indicating the APQIs of the APQNs to be verified * * Verifies that each APQN derived from the Cartesian product of APIDs * represented by the bits set in @mdev_apm and the APQIs of the bits set in * @mdev_aqm is not assigned to a mediated device other than the mdev to which * the APQN is being assigned (@assignee). AP queue sharing is not allowed. * * Return: 0 if the APQNs are not shared; otherwise return -EADDRINUSE.
*/ staticint vfio_ap_mdev_verify_no_sharing(struct ap_matrix_mdev *assignee, unsignedlong *mdev_apm, unsignedlong *mdev_aqm)
{ struct ap_matrix_mdev *assigned_to;
DECLARE_BITMAP(apm, AP_DEVICES);
DECLARE_BITMAP(aqm, AP_DOMAINS);
list_for_each_entry(assigned_to, &matrix_dev->mdev_list, node) { /* * If the mdev to which the mdev_apm and mdev_aqm is being * assigned is the same as the mdev being verified
*/ if (assignee == assigned_to) continue;
/* * We work on full longs, as we can only exclude the leftover * bits in non-inverse order. The leftover is all zeros.
*/ if (!bitmap_and(apm, mdev_apm, assigned_to->matrix.apm, AP_DEVICES)) continue;
if (!bitmap_and(aqm, mdev_aqm, assigned_to->matrix.aqm, AP_DOMAINS)) continue;
if (assignee)
vfio_ap_mdev_log_sharing_err(assignee, assigned_to, apm, aqm); else
vfio_ap_mdev_log_in_use_err(assigned_to, apm, aqm);
return -EADDRINUSE;
}
return 0;
}
/** * vfio_ap_mdev_validate_masks - verify that the APQNs assigned to the mdev are * not reserved for the default zcrypt driver and * are not assigned to another mdev. * * @matrix_mdev: the mdev to which the APQNs being validated are assigned. * * Return: One of the following values: * o the error returned from the ap_apqn_in_matrix_owned_by_def_drv() function, * most likely -EBUSY indicating the ap_perms_mutex lock is already held. * o EADDRNOTAVAIL if an APQN assigned to @matrix_mdev is reserved for the * zcrypt default driver. * o EADDRINUSE if an APQN assigned to @matrix_mdev is assigned to another mdev * o A zero indicating validation succeeded.
*/ staticint 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;
/** * assign_adapter_store - parses the APID from @buf and sets the * corresponding bit in the mediated matrix device's APM * * @dev: the matrix device * @attr: the mediated matrix device's assign_adapter attribute * @buf: a buffer containing the AP adapter number (APID) to * be assigned * @count: the number of bytes in @buf * * Return: the number of bytes processed if the APID is valid; otherwise, * returns one of the following errors: * * 1. -EINVAL * The APID is not a valid number * * 2. -ENODEV * The APID exceeds the maximum value configured for the system * * 3. -EADDRNOTAVAIL * An APQN derived from the cross product of the APID being assigned * and the APQIs previously assigned is not bound to the vfio_ap device * driver; or, if no APQIs have yet been assigned, the APID is not * contained in an APQN bound to the vfio_ap device driver. * * 4. -EADDRINUSE * An APQN derived from the cross product of the APID being assigned * and the APQIs previously assigned is being used by another mediated * matrix device * * 5. -EAGAIN * A lock required to validate the mdev's AP configuration could not * be obtained.
*/ static ssize_t assign_adapter_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int ret; unsignedlong apid;
DECLARE_BITMAP(apm_filtered, AP_DEVICES); struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
q = vfio_ap_mdev_get_queue(matrix_mdev, AP_MKQID(apid, apqi)); /* If the queue is assigned to the matrix mdev, unlink it. */ if (q)
vfio_ap_unlink_queue_fr_mdev(q);
return q;
}
/** * vfio_ap_mdev_unlink_adapter - unlink all queues associated with unassigned * adapter from the matrix mdev to which the * adapter was assigned. * @matrix_mdev: the matrix mediated device to which the adapter was assigned. * @apid: the APID of the unassigned adapter. * @qlist: list for storing queues associated with unassigned adapter that * need to be reset.
*/ staticvoid vfio_ap_mdev_unlink_adapter(struct ap_matrix_mdev *matrix_mdev, unsignedlong apid, struct list_head *qlist)
{ unsignedlong apqi; struct vfio_ap_queue *q;
/** * unassign_adapter_store - parses the APID from @buf and clears the * corresponding bit in the mediated matrix device's APM * * @dev: the matrix device * @attr: the mediated matrix device's unassign_adapter attribute * @buf: a buffer containing the adapter number (APID) to be unassigned * @count: the number of bytes in @buf * * Return: the number of bytes processed if the APID is valid; otherwise, * returns one of the following errors: * -EINVAL if the APID is not a number * -ENODEV if the APID it exceeds the maximum value configured for the * system
*/ static ssize_t unassign_adapter_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int ret; unsignedlong apid; struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
get_update_locks_for_mdev(matrix_mdev);
ret = kstrtoul(buf, 0, &apid); if (ret) goto done;
if (apid > matrix_mdev->matrix.apm_max) {
ret = -ENODEV; goto done;
}
if (!test_bit_inv(apid, matrix_mdev->matrix.apm)) {
ret = count; goto done;
}
/** * assign_domain_store - parses the APQI from @buf and sets the * corresponding bit in the mediated matrix device's AQM * * @dev: the matrix device * @attr: the mediated matrix device's assign_domain attribute * @buf: a buffer containing the AP queue index (APQI) of the domain to * be assigned * @count: the number of bytes in @buf * * Return: the number of bytes processed if the APQI is valid; otherwise returns * one of the following errors: * * 1. -EINVAL * The APQI is not a valid number * * 2. -ENODEV * The APQI exceeds the maximum value configured for the system * * 3. -EADDRNOTAVAIL * An APQN derived from the cross product of the APQI being assigned * and the APIDs previously assigned is not bound to the vfio_ap device * driver; or, if no APIDs have yet been assigned, the APQI is not * contained in an APQN bound to the vfio_ap device driver. * * 4. -EADDRINUSE * An APQN derived from the cross product of the APQI being assigned * and the APIDs previously assigned is being used by another mediated * matrix device * * 5. -EAGAIN * The lock required to validate the mdev's AP configuration could not * be obtained.
*/ static ssize_t assign_domain_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int ret; unsignedlong apqi;
DECLARE_BITMAP(apm_filtered, AP_DEVICES); struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
/** * unassign_domain_store - parses the APQI from @buf and clears the * corresponding bit in the mediated matrix device's AQM * * @dev: the matrix device * @attr: the mediated matrix device's unassign_domain attribute * @buf: a buffer containing the AP queue index (APQI) of the domain to * be unassigned * @count: the number of bytes in @buf * * Return: the number of bytes processed if the APQI is valid; otherwise, * returns one of the following errors: * -EINVAL if the APQI is not a number * -ENODEV if the APQI exceeds the maximum value configured for the system
*/ static ssize_t unassign_domain_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int ret; unsignedlong apqi; struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
get_update_locks_for_mdev(matrix_mdev);
ret = kstrtoul(buf, 0, &apqi); if (ret) goto done;
if (apqi > matrix_mdev->matrix.aqm_max) {
ret = -ENODEV; goto done;
}
if (!test_bit_inv(apqi, matrix_mdev->matrix.aqm)) {
ret = count; goto done;
}
clear_bit_inv((unsignedlong)apqi, matrix_mdev->matrix.aqm);
vfio_ap_mdev_hot_unplug_domain(matrix_mdev, apqi);
ret = count;
/** * assign_control_domain_store - parses the domain ID from @buf and sets * the corresponding bit in the mediated matrix device's ADM * * @dev: the matrix device * @attr: the mediated matrix device's assign_control_domain attribute * @buf: a buffer containing the domain ID to be assigned * @count: the number of bytes in @buf * * Return: the number of bytes processed if the domain ID is valid; otherwise, * returns one of the following errors: * -EINVAL if the ID is not a number * -ENODEV if the ID exceeds the maximum value configured for the system
*/ static ssize_t assign_control_domain_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int ret; unsignedlong id; struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
get_update_locks_for_mdev(matrix_mdev);
ret = kstrtoul(buf, 0, &id); if (ret) goto done;
if (id > matrix_mdev->matrix.adm_max) {
ret = -ENODEV; goto done;
}
if (test_bit_inv(id, matrix_mdev->matrix.adm)) {
ret = count; goto done;
}
/* Set the bit in the ADM (bitmask) corresponding to the AP control * domain number (id). The bits in the mask, from most significant to * least significant, correspond to IDs 0 up to the one less than the * number of control domains that can be assigned.
*/
set_bit_inv(id, matrix_mdev->matrix.adm); if (vfio_ap_mdev_filter_cdoms(matrix_mdev))
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
ret = count;
done:
release_update_locks_for_mdev(matrix_mdev); return ret;
} static DEVICE_ATTR_WO(assign_control_domain);
/** * unassign_control_domain_store - parses the domain ID from @buf and * clears the corresponding bit in the mediated matrix device's ADM * * @dev: the matrix device * @attr: the mediated matrix device's unassign_control_domain attribute * @buf: a buffer containing the domain ID to be unassigned * @count: the number of bytes in @buf * * Return: the number of bytes processed if the domain ID is valid; otherwise, * returns one of the following errors: * -EINVAL if the ID is not a number * -ENODEV if the ID exceeds the maximum value configured for the system
*/ static ssize_t unassign_control_domain_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ int ret; unsignedlong domid; struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(dev);
get_update_locks_for_mdev(matrix_mdev);
ret = kstrtoul(buf, 0, &domid); if (ret) goto done;
if (domid > matrix_mdev->matrix.adm_max) {
ret = -ENODEV; goto done;
}
if (!test_bit_inv(domid, matrix_mdev->matrix.adm)) {
ret = count; goto done;
}
clear_bit_inv(domid, matrix_mdev->matrix.adm);
if (test_bit_inv(domid, matrix_mdev->shadow_apcb.adm)) {
clear_bit_inv(domid, matrix_mdev->shadow_apcb.adm);
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
}
ret = count;
done:
release_update_locks_for_mdev(matrix_mdev); return ret;
} static DEVICE_ATTR_WO(unassign_control_domain);
/** * vfio_ap_mdev_set_kvm - sets all data for @matrix_mdev that are needed * to manage AP resources for the guest whose state is represented by @kvm * * @matrix_mdev: a mediated matrix device * @kvm: reference to KVM instance * * Return: 0 if no other mediated matrix device has a reference to @kvm; * otherwise, returns an -EPERM.
*/ staticint vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev, struct kvm *kvm)
{ struct ap_matrix_mdev *m;
if (kvm->arch.crypto.crycbd) {
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
queue = ap_get_qdev(apqn); if (!queue) return NULL;
if (queue->ap_dev.device.driver == &matrix_dev->vfio_ap_drv->driver)
q = dev_get_drvdata(&queue->ap_dev.device);
put_device(&queue->ap_dev.device);
return q;
}
staticint apq_status_check(int apqn, struct ap_queue_status *status)
{ switch (status->response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED: return 0; case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_BUSY: return -EBUSY; case AP_RESPONSE_ASSOC_SECRET_NOT_UNIQUE: case AP_RESPONSE_ASSOC_FAILED: /* * These asynchronous response codes indicate a PQAP(AAPQ) * instruction to associate a secret with the guest failed. All * subsequent AP instructions will end with the asynchronous * response code until the AP queue is reset; so, let's return * a value indicating a reset needs to be performed again.
*/ return -EAGAIN; default:
WARN(true, "failed to verify reset of queue %02x.%04x: TAPQ rc=%u\n",
AP_QID_CARD(apqn), AP_QID_QUEUE(apqn),
status->response_code); return -EIO;
}
}
#define WAIT_MSG "Waited %dms for reset of queue %02x.%04x (%u, %u, %u)"
staticvoid apq_reset_check(struct work_struct *reset_work)
{ int ret = -EBUSY, elapsed = 0; struct ap_queue_status status; struct vfio_ap_queue *q;
q = container_of(reset_work, struct vfio_ap_queue, reset_work);
memcpy(&status, &q->reset_status, sizeof(status)); while (true) {
msleep(AP_RESET_INTERVAL);
elapsed += AP_RESET_INTERVAL;
status = ap_tapq(q->apqn, NULL);
ret = apq_status_check(q->apqn, &status); if (ret == -EIO) return; if (ret == -EBUSY) {
pr_notice_ratelimited(WAIT_MSG, elapsed,
AP_QID_CARD(q->apqn),
AP_QID_QUEUE(q->apqn),
status.response_code,
status.queue_empty,
status.irq_enabled);
} else { if (q->reset_status.response_code == AP_RESPONSE_RESET_IN_PROGRESS ||
q->reset_status.response_code == AP_RESPONSE_BUSY ||
q->reset_status.response_code == AP_RESPONSE_STATE_CHANGE_IN_PROGRESS ||
ret == -EAGAIN) {
status = ap_zapq(q->apqn, 0);
memcpy(&q->reset_status, &status, sizeof(status)); continue;
} if (q->saved_isc != VFIO_AP_ISC_INVALID)
vfio_ap_free_aqic_resources(q); break;
}
}
}
if (!q) return;
status = ap_zapq(q->apqn, 0);
memcpy(&q->reset_status, &status, sizeof(status)); switch (status.response_code) { case AP_RESPONSE_NORMAL: case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_BUSY: case AP_RESPONSE_STATE_CHANGE_IN_PROGRESS: /* * Let's verify whether the ZAPQ completed successfully on a work queue.
*/
queue_work(system_long_wq, &q->reset_work); break; case AP_RESPONSE_DECONFIGURED: case AP_RESPONSE_CHECKSTOPPED:
vfio_ap_free_aqic_resources(q); break; default:
WARN(true, "PQAP/ZAPQ for %02x.%04x failed with invalid rc=%u\n",
AP_QID_CARD(q->apqn), AP_QID_QUEUE(q->apqn),
status.response_code);
}
}
staticint vfio_ap_mdev_reset_queues(struct ap_matrix_mdev *matrix_mdev)
{ int ret = 0, loop_cursor; struct vfio_ap_queue *q;
hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode)
vfio_ap_mdev_reset_queue(q);
hash_for_each(matrix_mdev->qtable.queues, loop_cursor, q, mdev_qnode) {
flush_work(&q->reset_work);
if (q->reset_status.response_code)
ret = -EIO;
}
return ret;
}
staticint vfio_ap_mdev_reset_qlist(struct list_head *qlist)
{ int ret = 0; struct vfio_ap_queue *q;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.