[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do
From: |
Alexey Kardashevskiy |
Subject: |
[Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB |
Date: |
Thu, 15 May 2014 19:59:59 +1000 |
Currently SPAPR PHB keeps track of all allocated MSI/MISX interrupt as
XICS used to be unable to reuse interrupts which becomes a problem for
dynamic MSI reconfiguration which is happening on guest driver reload or
PCI hot (un)plug. Another problem is that PHB has a limit of devices
supporting MSI/MSIX (SPAPR_MSIX_MAX_DEVS=32) and there is no good reason
for that.
This makes use of new XICS ability to reuse interrupts.
This removes cached MSI configuration from SPAPR PHB so the first IRQ number
of a device is stored in MSI/MSIX config space so there is no need to store
this anywhere else. From now on, SPAPR PHB only keeps flags telling what type
of interrupt for which device it has configured in order to return error if
(for example) MSIX was enabled and the guest is trying to disable MSI which
it has not enabled.
This removes a limit for the maximum number of MSIX-enabled devices per PHB,
now XICS and PCI bus capacity are the only limitation.
This changes migration stream as it fixes vmstate_spapr_pci_msi::name which was
wrong since the beginning.
This fixed traces to be more informative.
Signed-off-by: Alexey Kardashevskiy <address@hidden>
---
In reality either MSIX or MSI is enabled, never both. So I could remove msi/msix
bitmaps from this patch, would it make sense?
---
hw/ppc/spapr_pci.c | 179 +++++++++++++++++++++++---------------------
include/hw/pci-host/spapr.h | 11 +--
trace-events | 5 +-
3 files changed, 99 insertions(+), 96 deletions(-)
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index e574014..49e0382 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -220,36 +220,12 @@ static void rtas_write_pci_config(PowerPCCPU *cpu,
sPAPREnvironment *spapr,
}
/*
- * Find an entry with config_addr or returns the empty one if not found AND
- * alloc_new is set.
- * At the moment the msi_table entries are never released so there is
- * no point to look till the end of the list if we need to find the free entry.
- */
-static int spapr_msicfg_find(sPAPRPHBState *phb, uint32_t config_addr,
- bool alloc_new)
-{
- int i;
-
- for (i = 0; i < SPAPR_MSIX_MAX_DEVS; ++i) {
- if (!phb->msi_table[i].nvec) {
- break;
- }
- if (phb->msi_table[i].config_addr == config_addr) {
- return i;
- }
- }
- if ((i < SPAPR_MSIX_MAX_DEVS) && alloc_new) {
- trace_spapr_pci_msi("Allocating new MSI config", i, config_addr);
- return i;
- }
-
- return -1;
-}
-
-/*
* Set MSI/MSIX message data.
* This is required for msi_notify()/msix_notify() which
* will write at the addresses via spapr_msi_write().
+ *
+ * If hwaddr == 0, all entries will have .data == first_irq i.e.
+ * table will be reset.
*/
static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, bool msix,
unsigned first_irq, unsigned req_num)
@@ -263,12 +239,51 @@ static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr
addr, bool msix,
return;
}
- for (i = 0; i < req_num; ++i, ++msg.data) {
+ for (i = 0; i < req_num; ++i) {
msix_set_message(pdev, i, msg);
trace_spapr_pci_msi_setup(pdev->name, i, msg.address);
+ if (addr) {
+ ++msg.data;
+ }
}
}
+static unsigned spapr_msi_get(sPAPRPHBState *phb, PCIDevice *pdev,
+ unsigned *num, bool *msix)
+{
+ MSIMessage msg;
+ unsigned irq = 0;
+ uint8_t offs = (pci_bus_num(pdev->bus) << SPAPR_PCI_BUS_SHIFT) |
+ PCI_SLOT(pdev->devfn);
+
+ if ((phb->msi[offs] & (1 << PCI_FUNC(pdev->devfn))) &&
+ (phb->msix[offs] & (1 << PCI_FUNC(pdev->devfn)))) {
+ error_report("Both MSI and MSIX configured! MSIX will be used.");
+ }
+
+ if (phb->msix[offs] & (1 << PCI_FUNC(pdev->devfn))) {
+ *num = pdev->msix_entries_nr;
+ if (*num) {
+ msg = msix_get_message(pdev, 0);
+ irq = msg.data;
+ if (msix) {
+ *msix = true;
+ }
+ }
+ } else if (phb->msi[offs] & (1 << PCI_FUNC(pdev->devfn))) {
+ *num = msi_nr_vectors_allocated(pdev);
+ if (*num) {
+ msg = msi_get_message(pdev, 0);
+ irq = msg.data;
+ if (msix) {
+ *msix = false;
+ }
+ }
+ }
+
+ return irq;
+}
+
static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args, uint32_t nret,
@@ -280,9 +295,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu,
sPAPREnvironment *spapr,
unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */
unsigned int seq_num = rtas_ld(args, 5);
unsigned int ret_intr_type;
- int ndev, irq, max_irqs = 0;
+ unsigned int irq, max_irqs = 0, num = 0, offs;
sPAPRPHBState *phb = NULL;
PCIDevice *pdev = NULL;
+ bool msix = false;
switch (func) {
case RTAS_CHANGE_MSI_FN:
@@ -307,16 +323,29 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu,
sPAPREnvironment *spapr,
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
+ offs = (pci_bus_num(pdev->bus) << SPAPR_PCI_BUS_SHIFT) |
+ PCI_SLOT(pdev->devfn);
/* Releasing MSIs */
if (!req_num) {
- ndev = spapr_msicfg_find(phb, config_addr, false);
- if (ndev < 0) {
- trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr);
+ irq = spapr_msi_get(phb, pdev, &num, &msix);
+ if (!num || !irq ||
+ ((func == RTAS_CHANGE_MSI_FN) && msix) ||
+ ((func == RTAS_CHANGE_MSIX_FN) && !msix)) {
+ trace_spapr_pci_msi("Releasing wrong config", config_addr);
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
- trace_spapr_pci_msi("Released MSIs", ndev, config_addr);
+
+ xics_free(spapr->icp, 0, irq, num);
+ spapr_msi_setmsg(pdev, 0, msix, 0, num);
+
+ if (msix) {
+ phb->msix[offs] &= ~(1 << PCI_FUNC(pdev->devfn));
+ } else {
+ phb->msi[offs] &= ~(1 << PCI_FUNC(pdev->devfn));
+ }
+ trace_spapr_pci_msi("Released MSIs", config_addr);
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, 0);
return;
@@ -324,15 +353,6 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu,
sPAPREnvironment *spapr,
/* Enabling MSI */
- /* Find a device number in the map to add or reuse the existing one */
- ndev = spapr_msicfg_find(phb, config_addr, true);
- if (ndev >= SPAPR_MSIX_MAX_DEVS || ndev < 0) {
- error_report("No free entry for a new MSI device");
- rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
- return;
- }
- trace_spapr_pci_msi("Configuring MSI", ndev, config_addr);
-
/* Check if the device supports as many IRQs as requested */
if (ret_intr_type == RTAS_TYPE_MSI) {
max_irqs = msi_nr_vectors_allocated(pdev);
@@ -340,48 +360,44 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu,
sPAPREnvironment *spapr,
max_irqs = pdev->msix_entries_nr;
}
if (!max_irqs) {
- error_report("Requested interrupt type %d is not enabled for
device#%d",
- ret_intr_type, ndev);
+ error_report("Requested interrupt type %d is not enabled for device
%x",
+ ret_intr_type, config_addr);
rtas_st(rets, 0, -1); /* Hardware error */
return;
}
/* Correct the number if the guest asked for too many */
if (req_num > max_irqs) {
+ trace_spapr_pci_msi_retry(config_addr, req_num, max_irqs);
req_num = max_irqs;
+ irq = 0; /* to avoid misleading trace */
+ goto out;
}
- /* Check if there is an old config and MSI number has not changed */
- if (phb->msi_table[ndev].nvec && (req_num != phb->msi_table[ndev].nvec)) {
- /* Unexpected behaviour */
- error_report("Cannot reuse MSI config for device#%d", ndev);
+ /* Allocate MSIs */
+ irq = xics_alloc_block(spapr->icp, 0, req_num, false,
+ ret_intr_type == RTAS_TYPE_MSI);
+ if (!irq) {
+ error_report("Cannot allocate MSIs for device %x", config_addr);
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
- /* There is no cached config, allocate MSIs */
- if (!phb->msi_table[ndev].nvec) {
- irq = xics_alloc_block(spapr->icp, 0, req_num, false,
- ret_intr_type == RTAS_TYPE_MSI);
- if (irq < 0) {
- error_report("Cannot allocate MSIs for device#%d", ndev);
- rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
- return;
- }
- phb->msi_table[ndev].irq = irq;
- phb->msi_table[ndev].nvec = req_num;
- phb->msi_table[ndev].config_addr = config_addr;
- }
-
/* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */
spapr_msi_setmsg(pdev, spapr->msi_win_addr, ret_intr_type ==
RTAS_TYPE_MSIX,
- phb->msi_table[ndev].irq, req_num);
+ irq, req_num);
+ if (ret_intr_type == RTAS_TYPE_MSIX) {
+ phb->msix[offs] |= 1 << PCI_FUNC(pdev->devfn);
+ } else {
+ phb->msi[offs] |= 1 << PCI_FUNC(pdev->devfn);
+ }
+out:
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, req_num);
rtas_st(rets, 2, ++seq_num);
rtas_st(rets, 3, ret_intr_type);
- trace_spapr_pci_rtas_ibm_change_msi(func, req_num);
+ trace_spapr_pci_rtas_ibm_change_msi(config_addr, func, req_num, irq);
}
static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
@@ -395,25 +411,28 @@ static void
rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu,
uint32_t config_addr = rtas_ld(args, 0);
uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2);
unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3);
- int ndev;
+ unsigned irq, num = 0;
sPAPRPHBState *phb = NULL;
+ PCIDevice *pdev = NULL;
/* Fins sPAPRPHBState */
phb = find_phb(spapr, buid);
- if (!phb) {
+ if (phb) {
+ pdev = find_dev(spapr, buid, config_addr);
+ }
+ if (!phb || !pdev) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
return;
}
/* Find device descriptor and start IRQ */
- ndev = spapr_msicfg_find(phb, config_addr, false);
- if (ndev < 0) {
- trace_spapr_pci_msi("MSI has not been enabled", -1, config_addr);
+ irq = spapr_msi_get(phb, pdev, &num, NULL);
+ if (!irq || !num || (ioa_intr_num >= num)) {
+ trace_spapr_pci_msi("Failed to return vector", config_addr);
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
-
- intr_src_num = phb->msi_table[ndev].irq + ioa_intr_num;
+ intr_src_num = irq + ioa_intr_num;
trace_spapr_pci_rtas_ibm_query_interrupt_source_number(ioa_intr_num,
intr_src_num);
@@ -675,20 +694,6 @@ static const VMStateDescription vmstate_spapr_pci_lsi = {
},
};
-static const VMStateDescription vmstate_spapr_pci_msi = {
- .name = "spapr_pci/lsi",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField []) {
- VMSTATE_UINT32(config_addr, struct spapr_pci_msi),
- VMSTATE_UINT32(irq, struct spapr_pci_msi),
- VMSTATE_UINT32(nvec, struct spapr_pci_msi),
-
- VMSTATE_END_OF_LIST()
- },
-};
-
static const VMStateDescription vmstate_spapr_pci = {
.name = "spapr_pci",
.version_id = 1,
@@ -703,8 +708,8 @@ static const VMStateDescription vmstate_spapr_pci = {
VMSTATE_UINT64_EQUAL(io_win_size, sPAPRPHBState),
VMSTATE_STRUCT_ARRAY(lsi_table, sPAPRPHBState, PCI_NUM_PINS, 0,
vmstate_spapr_pci_lsi, struct spapr_pci_lsi),
- VMSTATE_STRUCT_ARRAY(msi_table, sPAPRPHBState, SPAPR_MSIX_MAX_DEVS, 0,
- vmstate_spapr_pci_msi, struct spapr_pci_msi),
+ VMSTATE_UINT8_ARRAY(msi, sPAPRPHBState, PCI_BUS_MAX * PCI_SLOT_MAX),
+ VMSTATE_UINT8_ARRAY(msix, sPAPRPHBState, PCI_BUS_MAX * PCI_SLOT_MAX),
VMSTATE_END_OF_LIST()
},
diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h
index 970b4a9..7ef29ab 100644
--- a/include/hw/pci-host/spapr.h
+++ b/include/hw/pci-host/spapr.h
@@ -27,8 +27,6 @@
#include "hw/pci/pci_host.h"
#include "hw/ppc/xics.h"
-#define SPAPR_MSIX_MAX_DEVS 32
-
#define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge"
#define SPAPR_PCI_HOST_BRIDGE(obj) \
@@ -55,11 +53,10 @@ typedef struct sPAPRPHBState {
uint32_t irq;
} lsi_table[PCI_NUM_PINS];
- struct spapr_pci_msi {
- uint32_t config_addr;
- uint32_t irq;
- uint32_t nvec;
- } msi_table[SPAPR_MSIX_MAX_DEVS];
+#define PCI_BUS_MAX 0xFF
+#define SPAPR_PCI_BUS_SHIFT 5
+ uint8_t msi[PCI_BUS_MAX * PCI_SLOT_MAX];
+ uint8_t msix[PCI_BUS_MAX * PCI_SLOT_MAX];
QLIST_ENTRY(sPAPRPHBState) list;
} sPAPRPHBState;
diff --git a/trace-events b/trace-events
index 0a66c71..c180ea1 100644
--- a/trace-events
+++ b/trace-events
@@ -1159,12 +1159,13 @@ qxl_render_guest_primary_resized(int32_t width, int32_t
height, int32_t stride,
qxl_render_update_area_done(void *cookie) "%p"
# hw/ppc/spapr_pci.c
-spapr_pci_msi(const char *msg, uint32_t n, uint32_t ca) "%s (device#%d,
cfg=%x)"
+spapr_pci_msi(const char *msg, uint32_t ca) "%s (cfg=%x)"
spapr_pci_msi_setup(const char *name, unsigned vector, uint64_t addr)
"dev\"%s\" vector %u, addr=%"PRIx64
-spapr_pci_rtas_ibm_change_msi(unsigned func, unsigned req) "func %u, requested
%u"
+spapr_pci_rtas_ibm_change_msi(unsigned cfg, unsigned func, unsigned req,
unsigned first) "cfgaddr %x func %u, requested %u, first irq %u"
spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned intr)
"queries for #%u, IRQ%u"
spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq)
"@%"PRIx64"<=%"PRIx64" IRQ %u"
spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ %u"
+spapr_pci_msi_retry(unsigned config_addr, unsigned req_num, unsigned max_irqs)
"Guest device at %x asked %u, have only %u"
# hw/intc/xics.c
xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=%#x"
--
1.9.rc0
- [Qemu-ppc] [PATCH v2 4/8] spapr: Move interrupt allocator to xics, (continued)
- [Qemu-ppc] [PATCH v2 6/8] spapr: Remove @next_irq, Alexey Kardashevskiy, 2014/05/15
- [Qemu-ppc] [PATCH v2 1/8] xics: Add flags for interrupts, Alexey Kardashevskiy, 2014/05/15
- [Qemu-ppc] [PATCH v2 7/8] xics: Implement xics_ics_free(), Alexey Kardashevskiy, 2014/05/15
- [Qemu-ppc] [PATCH v2 5/8] xics: Remove obsolete xics_set_irq_type(), Alexey Kardashevskiy, 2014/05/15
- [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB,
Alexey Kardashevskiy <=
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexander Graf, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexey Kardashevskiy, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexander Graf, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Michael S. Tsirkin, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexander Graf, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexey Kardashevskiy, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Michael S. Tsirkin, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexey Kardashevskiy, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexander Graf, 2014/05/21
- Re: [Qemu-ppc] [PATCH v2 8/8] spapr_pci: Use XICS interrupt allocator and do not cache interrupts in PHB, Alexey Kardashevskiy, 2014/05/21