@@ -423,6 +423,29 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
rtas_st(rets, 2, 1);/* 0 == level; 1 == edge */
}
+static sPAPRPHBGuestPE *spapr_phb_pe_by_device(sPAPRPHBState *phb,
+ PCIDevice *pdev)
+{
+ VFIOGroup *group = vfio_pci_device_group(pdev);
+ sPAPRPHBGuestPE *pe;
+
+ if (!group) {
+ return NULL;
+ }
+
+ QLIST_FOREACH(pe, &phb->pe_list, list) {
+ if (pe->group == group) {
+ /* We already have a PE for this group */
+ return pe;
+ }
+ }
+
+ pe = g_malloc0(sizeof(*pe));
+ pe->group = group;
+ QLIST_INSERT_HEAD(&phb->pe_list, pe, list);
+ return pe;
+}
+
static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu,
sPAPRMachineState *spapr,
uint32_t token, uint32_t nargs,
@@ -1200,8 +1223,14 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc,
if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) {
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn);
+ sPAPRPHBGuestPE *pe;
spapr_tce_need_vfio(tcet);
+
+ pe = spapr_phb_pe_by_device(phb, pdev);
+ if (pe) {
+ pe->num_devices++;
+ }
}
if (dev->hotplugged) {
@@ -1243,6 +1272,14 @@ static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc,
Error **errp)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ sPAPRPHBGuestPE *pe =spapr_phb_pe_by_device(phb, pdev);
+
+ if (pe) {
+ if (--pe->num_devices == 0) {
+ QLIST_REMOVE(pe, list);
+ g_free(pe);
+ }
+ }
drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp);
}
@@ -1501,6 +1538,8 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
info->finish_realize(sphb, errp);
sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
+
+ QLIST_INIT(&sphb->pe_list);
}
static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp)
@@ -62,6 +62,12 @@ typedef struct spapr_pci_msi_mig {
spapr_pci_msi value;
} spapr_pci_msi_mig;
+typedef struct sPAPRPHBGuestPE {
+ int num_devices;
+ VFIOGroup *group;
+ QLIST_ENTRY(sPAPRPHBGuestPE) list;
+} sPAPRPHBGuestPE;
+
struct sPAPRPHBState {
PCIHostState parent_obj;
@@ -88,6 +94,8 @@ struct sPAPRPHBState {
int32_t msi_devs_num;
spapr_pci_msi_mig *msi_devs;
+ QLIST_HEAD(, sPAPRPHBGuestPE) pe_list;
+
QLIST_ENTRY(sPAPRPHBState) list;
};
PAPR has a concept of Partitionable Endpoints (PEs) on PCI, which are groups of devices that can't be isolated from each other, which is used in the Enhanced Error Handling (EEH) interface. At the moment, we don't model PEs in our PAPR host bridge implementation. We get away with it because spapr-pci-host-bridge doesn't support the EEH interfaces, and spapr-pci-vfio-host-bridge, which does, only supports devices from a single IOMMU group, and therefore a single PE (although that's not actually properly enforced). In order to generalize this, this adds tracking of guest PEs to the PAPR host bridge code. For now, at least, we only construct PEs for VFIO devices, since we don't implement EEH for emulated devices anyway PEs. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> --- hw/ppc/spapr_pci.c | 39 +++++++++++++++++++++++++++++++++++++++ include/hw/pci-host/spapr.h | 8 ++++++++ 2 files changed, 47 insertions(+)