Skip to content

Commit 4a8d21b

Browse files
rosatomjhuth
authored andcommitted
s390x/pci: RPCIT second pass when mappings exhausted
If we encounter a new mapping while the number of available DMA entries in vfio is 0, we are currently skipping that mapping which is a problem if we manage to free up DMA space after that within the same RPCIT -- we will return to the guest with CC0 and have not mapped everything within the specified range. This issue was uncovered while testing changes to the s390 linux kernel iommu/dma code, where a different usage pattern was employed (new mappings start at the end of the aperture and work back towards the front, making us far more likely to encounter new mappings before invalidated mappings during a global refresh). Fix this by tracking whether any mappings were skipped due to vfio DMA limit hitting 0; when this occurs, we still continue the range and unmap/map anything we can - then we must re-run the range again to pickup anything that was missed. This must occur in a loop until all requests are satisfied (success) or we detect that we are still unable to complete all mappings (return ZPCI_RPCIT_ST_INSUFF_RES). Link: https://lore.kernel.org/linux-s390/[email protected]/ Fixes: 37fa32d ("s390x/pci: Honor DMA limits set by vfio") Reported-by: Niklas Schnelle <[email protected]> Signed-off-by: Matthew Rosato <[email protected]> Message-Id: <[email protected]> Reviewed-by: Eric Farman <[email protected]> Signed-off-by: Thomas Huth <[email protected]>
1 parent f53b033 commit 4a8d21b

File tree

1 file changed

+22
-7
lines changed

1 file changed

+22
-7
lines changed

hw/s390x/s390-pci-inst.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -677,8 +677,9 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
677677
S390PCIBusDevice *pbdev;
678678
S390PCIIOMMU *iommu;
679679
S390IOTLBEntry entry;
680-
hwaddr start, end;
680+
hwaddr start, end, sstart;
681681
uint32_t dma_avail;
682+
bool again;
682683

683684
if (env->psw.mask & PSW_MASK_PSTATE) {
684685
s390_program_interrupt(env, PGM_PRIVILEGED, ra);
@@ -691,7 +692,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
691692
}
692693

693694
fh = env->regs[r1] >> 32;
694-
start = env->regs[r2];
695+
sstart = start = env->regs[r2];
695696
end = start + env->regs[r2 + 1];
696697

697698
pbdev = s390_pci_find_dev_by_fh(s390_get_phb(), fh);
@@ -732,20 +733,34 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
732733
goto err;
733734
}
734735

736+
retry:
737+
start = sstart;
738+
again = false;
735739
while (start < end) {
736740
error = s390_guest_io_table_walk(iommu->g_iota, start, &entry);
737741
if (error) {
738742
break;
739743
}
740744

741745
start += entry.len;
742-
while (entry.iova < start && entry.iova < end &&
743-
(dma_avail > 0 || entry.perm == IOMMU_NONE)) {
744-
dma_avail = s390_pci_update_iotlb(iommu, &entry);
745-
entry.iova += TARGET_PAGE_SIZE;
746-
entry.translated_addr += TARGET_PAGE_SIZE;
746+
while (entry.iova < start && entry.iova < end) {
747+
if (dma_avail > 0 || entry.perm == IOMMU_NONE) {
748+
dma_avail = s390_pci_update_iotlb(iommu, &entry);
749+
entry.iova += TARGET_PAGE_SIZE;
750+
entry.translated_addr += TARGET_PAGE_SIZE;
751+
} else {
752+
/*
753+
* We are unable to make a new mapping at this time, continue
754+
* on and hopefully free up more space. Then attempt another
755+
* pass.
756+
*/
757+
again = true;
758+
break;
759+
}
747760
}
748761
}
762+
if (again && dma_avail > 0)
763+
goto retry;
749764
err:
750765
if (error) {
751766
pbdev->state = ZPCI_FS_ERROR;

0 commit comments

Comments
 (0)