358 lines
12 KiB
Diff
358 lines
12 KiB
Diff
From 7ef9b9c593da98ad32ad20c28d17bb2700a35c29 Mon Sep 17 00:00:00 2001
|
|
From: Cornelia Huck <cohuck@redhat.com>
|
|
Date: Tue, 19 Jan 2021 12:50:45 -0500
|
|
Subject: [PATCH 6/7] s390x/pci: Honor DMA limits set by vfio
|
|
|
|
RH-Author: Cornelia Huck <cohuck@redhat.com>
|
|
Message-id: <20210119125046.472811-7-cohuck@redhat.com>
|
|
Patchwork-id: 100680
|
|
O-Subject: [RHEL-8.4.0 qemu-kvm PATCH 6/7] s390x/pci: Honor DMA limits set by vfio
|
|
Bugzilla: 1905391
|
|
RH-Acked-by: David Hildenbrand <david@redhat.com>
|
|
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
|
|
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
|
From: Matthew Rosato <mjrosato@linux.ibm.com>
|
|
|
|
When an s390 guest is using lazy unmapping, it can result in a very
|
|
large number of oustanding DMA requests, far beyond the default
|
|
limit configured for vfio. Let's track DMA usage similar to vfio
|
|
in the host, and trigger the guest to flush their DMA mappings
|
|
before vfio runs out.
|
|
|
|
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
|
|
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
|
|
[aw: non-Linux build fixes]
|
|
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
|
|
(cherry picked from commit 37fa32de707340f3a93959ad5a1ebc41ba1520ee)
|
|
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
|
|
|
|
Conflicts:
|
|
hw/s390x/s390-pci-bus.c
|
|
--> adapt to missing 981c3dcd9489 ("qdev: Convert to
|
|
qdev_unrealize() with Coccinelle")
|
|
hw/s390x/s390-pci-inst.c
|
|
--> adapt to out of order inclusion of 5039caf3c449 ("memory:
|
|
Add IOMMUTLBEvent")
|
|
include/hw/s390x/s390-pci-bus.h
|
|
--> adapt to missing db1015e92e04 ("Move QOM typedefs and
|
|
add missing includes")
|
|
|
|
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
|
---
|
|
hw/s390x/s390-pci-bus.c | 16 ++++++++----
|
|
hw/s390x/s390-pci-inst.c | 45 +++++++++++++++++++++++++++-----
|
|
hw/s390x/s390-pci-vfio.c | 42 +++++++++++++++++++++++++++++
|
|
include/hw/s390x/s390-pci-bus.h | 9 +++++++
|
|
include/hw/s390x/s390-pci-inst.h | 3 +++
|
|
include/hw/s390x/s390-pci-vfio.h | 12 +++++++++
|
|
6 files changed, 116 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
|
|
index 6daef2b6d57..a9f6f550472 100644
|
|
--- a/hw/s390x/s390-pci-bus.c
|
|
+++ b/hw/s390x/s390-pci-bus.c
|
|
@@ -17,6 +17,7 @@
|
|
#include "cpu.h"
|
|
#include "hw/s390x/s390-pci-bus.h"
|
|
#include "hw/s390x/s390-pci-inst.h"
|
|
+#include "hw/s390x/s390-pci-vfio.h"
|
|
#include "hw/pci/pci_bus.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "hw/pci/pci_bridge.h"
|
|
@@ -771,6 +772,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp)
|
|
s->bus_no = 0;
|
|
QTAILQ_INIT(&s->pending_sei);
|
|
QTAILQ_INIT(&s->zpci_devs);
|
|
+ QTAILQ_INIT(&s->zpci_dma_limit);
|
|
|
|
css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false,
|
|
S390_ADAPTER_SUPPRESSIBLE, &local_err);
|
|
@@ -951,17 +953,18 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|
}
|
|
}
|
|
|
|
+ pbdev->pdev = pdev;
|
|
+ pbdev->iommu = s390_pci_get_iommu(s, pci_get_bus(pdev), pdev->devfn);
|
|
+ pbdev->iommu->pbdev = pbdev;
|
|
+ pbdev->state = ZPCI_FS_DISABLED;
|
|
+
|
|
if (object_dynamic_cast(OBJECT(dev), "vfio-pci")) {
|
|
pbdev->fh |= FH_SHM_VFIO;
|
|
+ pbdev->iommu->dma_limit = s390_pci_start_dma_count(s, pbdev);
|
|
} else {
|
|
pbdev->fh |= FH_SHM_EMUL;
|
|
}
|
|
|
|
- pbdev->pdev = pdev;
|
|
- pbdev->iommu = s390_pci_get_iommu(s, pci_get_bus(pdev), pdev->devfn);
|
|
- pbdev->iommu->pbdev = pbdev;
|
|
- pbdev->state = ZPCI_FS_DISABLED;
|
|
-
|
|
if (s390_pci_msix_init(pbdev)) {
|
|
error_setg(errp, "MSI-X support is mandatory "
|
|
"in the S390 architecture");
|
|
@@ -1014,6 +1017,9 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|
pbdev->fid = 0;
|
|
QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
|
|
g_hash_table_remove(s->zpci_table, &pbdev->idx);
|
|
+ if (pbdev->iommu->dma_limit) {
|
|
+ s390_pci_end_dma_count(s, pbdev->iommu->dma_limit);
|
|
+ }
|
|
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
|
|
}
|
|
}
|
|
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
|
|
index b1885344f18..edbdf727984 100644
|
|
--- a/hw/s390x/s390-pci-inst.c
|
|
+++ b/hw/s390x/s390-pci-inst.c
|
|
@@ -32,6 +32,20 @@
|
|
} \
|
|
} while (0)
|
|
|
|
+static inline void inc_dma_avail(S390PCIIOMMU *iommu)
|
|
+{
|
|
+ if (iommu->dma_limit) {
|
|
+ iommu->dma_limit->avail++;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline void dec_dma_avail(S390PCIIOMMU *iommu)
|
|
+{
|
|
+ if (iommu->dma_limit) {
|
|
+ iommu->dma_limit->avail--;
|
|
+ }
|
|
+}
|
|
+
|
|
static void s390_set_status_code(CPUS390XState *env,
|
|
uint8_t r, uint64_t status_code)
|
|
{
|
|
@@ -572,7 +586,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
|
|
return 0;
|
|
}
|
|
|
|
-static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry)
|
|
+static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
|
|
+ S390IOTLBEntry *entry)
|
|
{
|
|
S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
|
|
IOMMUTLBEvent event = {
|
|
@@ -588,14 +603,15 @@ static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry)
|
|
|
|
if (event.type == IOMMU_NOTIFIER_UNMAP) {
|
|
if (!cache) {
|
|
- return;
|
|
+ goto out;
|
|
}
|
|
g_hash_table_remove(iommu->iotlb, &entry->iova);
|
|
+ inc_dma_avail(iommu);
|
|
} else {
|
|
if (cache) {
|
|
if (cache->perm == entry->perm &&
|
|
cache->translated_addr == entry->translated_addr) {
|
|
- return;
|
|
+ goto out;
|
|
}
|
|
|
|
event.type = IOMMU_NOTIFIER_UNMAP;
|
|
@@ -611,9 +627,13 @@ static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry)
|
|
cache->len = PAGE_SIZE;
|
|
cache->perm = entry->perm;
|
|
g_hash_table_replace(iommu->iotlb, &cache->iova, cache);
|
|
+ dec_dma_avail(iommu);
|
|
}
|
|
|
|
memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
|
|
+
|
|
+out:
|
|
+ return iommu->dma_limit ? iommu->dma_limit->avail : 1;
|
|
}
|
|
|
|
int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
|
|
@@ -625,6 +645,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
|
|
S390PCIIOMMU *iommu;
|
|
S390IOTLBEntry entry;
|
|
hwaddr start, end;
|
|
+ uint32_t dma_avail;
|
|
|
|
if (env->psw.mask & PSW_MASK_PSTATE) {
|
|
s390_program_interrupt(env, PGM_PRIVILEGED, ra);
|
|
@@ -663,6 +684,11 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
|
|
}
|
|
|
|
iommu = pbdev->iommu;
|
|
+ if (iommu->dma_limit) {
|
|
+ dma_avail = iommu->dma_limit->avail;
|
|
+ } else {
|
|
+ dma_avail = 1;
|
|
+ }
|
|
if (!iommu->g_iota) {
|
|
error = ERR_EVENT_INVALAS;
|
|
goto err;
|
|
@@ -680,8 +706,9 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
|
|
}
|
|
|
|
start += entry.len;
|
|
- while (entry.iova < start && entry.iova < end) {
|
|
- s390_pci_update_iotlb(iommu, &entry);
|
|
+ while (entry.iova < start && entry.iova < end &&
|
|
+ (dma_avail > 0 || entry.perm == IOMMU_NONE)) {
|
|
+ dma_avail = s390_pci_update_iotlb(iommu, &entry);
|
|
entry.iova += PAGE_SIZE;
|
|
entry.translated_addr += PAGE_SIZE;
|
|
}
|
|
@@ -694,7 +721,13 @@ err:
|
|
s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, start, 0);
|
|
} else {
|
|
pbdev->fmb.counter[ZPCI_FMB_CNT_RPCIT]++;
|
|
- setcc(cpu, ZPCI_PCI_LS_OK);
|
|
+ if (dma_avail > 0) {
|
|
+ setcc(cpu, ZPCI_PCI_LS_OK);
|
|
+ } else {
|
|
+ /* vfio DMA mappings are exhausted, trigger a RPCIT */
|
|
+ setcc(cpu, ZPCI_PCI_LS_ERR);
|
|
+ s390_set_status_code(env, r1, ZPCI_RPCIT_ST_INSUFF_RES);
|
|
+ }
|
|
}
|
|
return 0;
|
|
}
|
|
diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c
|
|
index 0eb22ffec4c..01c1e8ac89a 100644
|
|
--- a/hw/s390x/s390-pci-vfio.c
|
|
+++ b/hw/s390x/s390-pci-vfio.c
|
|
@@ -12,7 +12,9 @@
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "qemu/osdep.h"
|
|
+#include "hw/s390x/s390-pci-bus.h"
|
|
#include "hw/s390x/s390-pci-vfio.h"
|
|
+#include "hw/vfio/pci.h"
|
|
#include "hw/vfio/vfio-common.h"
|
|
|
|
/*
|
|
@@ -52,3 +54,43 @@ retry:
|
|
return vfio_get_info_dma_avail(info, avail);
|
|
}
|
|
|
|
+S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s,
|
|
+ S390PCIBusDevice *pbdev)
|
|
+{
|
|
+ S390PCIDMACount *cnt;
|
|
+ uint32_t avail;
|
|
+ VFIOPCIDevice *vpdev = container_of(pbdev->pdev, VFIOPCIDevice, pdev);
|
|
+ int id;
|
|
+
|
|
+ assert(vpdev);
|
|
+
|
|
+ id = vpdev->vbasedev.group->container->fd;
|
|
+
|
|
+ if (!s390_pci_update_dma_avail(id, &avail)) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ QTAILQ_FOREACH(cnt, &s->zpci_dma_limit, link) {
|
|
+ if (cnt->id == id) {
|
|
+ cnt->users++;
|
|
+ return cnt;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ cnt = g_new0(S390PCIDMACount, 1);
|
|
+ cnt->id = id;
|
|
+ cnt->users = 1;
|
|
+ cnt->avail = avail;
|
|
+ QTAILQ_INSERT_TAIL(&s->zpci_dma_limit, cnt, link);
|
|
+ return cnt;
|
|
+}
|
|
+
|
|
+void s390_pci_end_dma_count(S390pciState *s, S390PCIDMACount *cnt)
|
|
+{
|
|
+ assert(cnt);
|
|
+
|
|
+ cnt->users--;
|
|
+ if (cnt->users == 0) {
|
|
+ QTAILQ_REMOVE(&s->zpci_dma_limit, cnt, link);
|
|
+ }
|
|
+}
|
|
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
|
|
index 550f3cc5e92..2f2edbd0bf3 100644
|
|
--- a/include/hw/s390x/s390-pci-bus.h
|
|
+++ b/include/hw/s390x/s390-pci-bus.h
|
|
@@ -266,6 +266,13 @@ typedef struct S390IOTLBEntry {
|
|
} S390IOTLBEntry;
|
|
|
|
typedef struct S390PCIBusDevice S390PCIBusDevice;
|
|
+typedef struct S390PCIDMACount {
|
|
+ int id;
|
|
+ int users;
|
|
+ uint32_t avail;
|
|
+ QTAILQ_ENTRY(S390PCIDMACount) link;
|
|
+} S390PCIDMACount;
|
|
+
|
|
typedef struct S390PCIIOMMU {
|
|
Object parent_obj;
|
|
S390PCIBusDevice *pbdev;
|
|
@@ -277,6 +284,7 @@ typedef struct S390PCIIOMMU {
|
|
uint64_t pba;
|
|
uint64_t pal;
|
|
GHashTable *iotlb;
|
|
+ S390PCIDMACount *dma_limit;
|
|
} S390PCIIOMMU;
|
|
|
|
typedef struct S390PCIIOMMUTable {
|
|
@@ -352,6 +360,7 @@ typedef struct S390pciState {
|
|
GHashTable *zpci_table;
|
|
QTAILQ_HEAD(, SeiContainer) pending_sei;
|
|
QTAILQ_HEAD(, S390PCIBusDevice) zpci_devs;
|
|
+ QTAILQ_HEAD(, S390PCIDMACount) zpci_dma_limit;
|
|
} S390pciState;
|
|
|
|
S390pciState *s390_get_phb(void);
|
|
diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
|
|
index fa3bf8b5aad..8ee3a3c2375 100644
|
|
--- a/include/hw/s390x/s390-pci-inst.h
|
|
+++ b/include/hw/s390x/s390-pci-inst.h
|
|
@@ -254,6 +254,9 @@ typedef struct ClpReqRspQueryPciGrp {
|
|
#define ZPCI_STPCIFC_ST_INVAL_DMAAS 28
|
|
#define ZPCI_STPCIFC_ST_ERROR_RECOVER 40
|
|
|
|
+/* Refresh PCI Translations status codes */
|
|
+#define ZPCI_RPCIT_ST_INSUFF_RES 16
|
|
+
|
|
/* FIB function controls */
|
|
#define ZPCI_FIB_FC_ENABLED 0x80
|
|
#define ZPCI_FIB_FC_ERROR 0x40
|
|
diff --git a/include/hw/s390x/s390-pci-vfio.h b/include/hw/s390x/s390-pci-vfio.h
|
|
index 1727292e9b5..539bcf04eb5 100644
|
|
--- a/include/hw/s390x/s390-pci-vfio.h
|
|
+++ b/include/hw/s390x/s390-pci-vfio.h
|
|
@@ -12,13 +12,25 @@
|
|
#ifndef HW_S390_PCI_VFIO_H
|
|
#define HW_S390_PCI_VFIO_H
|
|
|
|
+#include "hw/s390x/s390-pci-bus.h"
|
|
+
|
|
#ifdef CONFIG_LINUX
|
|
bool s390_pci_update_dma_avail(int fd, unsigned int *avail);
|
|
+S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s,
|
|
+ S390PCIBusDevice *pbdev);
|
|
+void s390_pci_end_dma_count(S390pciState *s, S390PCIDMACount *cnt);
|
|
#else
|
|
static inline bool s390_pci_update_dma_avail(int fd, unsigned int *avail)
|
|
{
|
|
return false;
|
|
}
|
|
+static inline S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s,
|
|
+ S390PCIBusDevice *pbdev)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+static inline void s390_pci_end_dma_count(S390pciState *s,
|
|
+ S390PCIDMACount *cnt) { }
|
|
#endif
|
|
|
|
#endif
|
|
--
|
|
2.27.0
|
|
|