aboutsummaryrefslogtreecommitdiff
path: root/hw/s390x/s390-pci-inst.c
diff options
context:
space:
mode:
authorYi Min Zhao <zyimin@linux.ibm.com>2019-01-08 18:37:30 +0100
committerCornelia Huck <cohuck@redhat.com>2019-01-18 11:52:01 +0100
commit6e92c70c37547b6a247a206651dfcc583a57f484 (patch)
treeafceec8e082e87d26d8117173b1ed63b01795319 /hw/s390x/s390-pci-inst.c
parent3549f8c9e4f0ef1c3417ff43b2164f68ad34b922 (diff)
s390x/pci: add common function measurement block
Common function measurement block is used to report zPCI internal counters of successful pcilg/stg/stb and rpcit instructions to a memory location provided by the program. This patch introduces a new ZpciFmb structure and schedules a timer callback to copy the zPCI measures to the FMB in the guest memory at an interval time set to 4s. An error while attemping to update the FMB, would generate an error event to the guest. The pcilg/stg/stb and rpcit interception handlers increase the related counter on a successful call. The guest shall pass a null FMBA (FMB address) in the FIB (Function Information Block) when it issues a Modify PCI Function Control instruction to switch off FMB and stop the corresponding timer. Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com> Signed-off-by: Pierre Morel <pmorel@linux.ibm.com> Message-Id: <1546969050-8884-2-git-send-email-pmorel@linux.ibm.com> Acked-by: David Hildenbrand <david@redhat.com> Reviewed-by: Collin Walling <walling@linux.ibm.com> Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Diffstat (limited to 'hw/s390x/s390-pci-inst.c')
-rw-r--r--hw/s390x/s390-pci-inst.c133
1 files changed, 130 insertions, 3 deletions
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 7b61367ee3..be2896232d 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -19,6 +19,7 @@
#include "exec/memory-internal.h"
#include "qemu/error-report.h"
#include "sysemu/hw_accel.h"
+#include "hw/s390x/tod.h"
#ifndef DEBUG_S390PCI_INST
#define DEBUG_S390PCI_INST 0
@@ -293,7 +294,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
resgrp->fr = 1;
stq_p(&resgrp->dasm, 0);
stq_p(&resgrp->msia, ZPCI_MSI_ADDR);
- stw_p(&resgrp->mui, 0);
+ stw_p(&resgrp->mui, DEFAULT_MUI);
stw_p(&resgrp->i, 128);
stw_p(&resgrp->maxstbl, 128);
resgrp->version = 0;
@@ -456,6 +457,8 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0;
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_LD]++;
+
env->regs[r1] = data;
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
@@ -561,6 +564,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0;
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_ST]++;
+
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
}
@@ -681,6 +686,7 @@ err:
s390_set_status_code(env, r1, ZPCI_PCI_ST_FUNC_IN_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);
}
return 0;
@@ -783,6 +789,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr,
}
}
+ pbdev->fmb.counter[ZPCI_FMB_CNT_STB]++;
+
setcc(cpu, ZPCI_PCI_LS_OK);
return 0;
@@ -889,6 +897,99 @@ void pci_dereg_ioat(S390PCIIOMMU *iommu)
iommu->g_iota = 0;
}
+void fmb_timer_free(S390PCIBusDevice *pbdev)
+{
+ if (pbdev->fmb_timer) {
+ timer_del(pbdev->fmb_timer);
+ timer_free(pbdev->fmb_timer);
+ pbdev->fmb_timer = NULL;
+ }
+ pbdev->fmb_addr = 0;
+ memset(&pbdev->fmb, 0, sizeof(ZpciFmb));
+}
+
+static int fmb_do_update(S390PCIBusDevice *pbdev, int offset, uint64_t val,
+ int len)
+{
+ MemTxResult ret;
+ uint64_t dst = pbdev->fmb_addr + offset;
+
+ switch (len) {
+ case 8:
+ address_space_stq_be(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ case 4:
+ address_space_stl_be(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ case 2:
+ address_space_stw_be(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ case 1:
+ address_space_stb(&address_space_memory, dst, val,
+ MEMTXATTRS_UNSPECIFIED,
+ &ret);
+ break;
+ default:
+ ret = MEMTX_ERROR;
+ break;
+ }
+ if (ret != MEMTX_OK) {
+ s390_pci_generate_error_event(ERR_EVENT_FMBA, pbdev->fh, pbdev->fid,
+ pbdev->fmb_addr, 0);
+ fmb_timer_free(pbdev);
+ }
+
+ return ret;
+}
+
+static void fmb_update(void *opaque)
+{
+ S390PCIBusDevice *pbdev = opaque;
+ int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
+ int i;
+
+ /* Update U bit */
+ pbdev->fmb.last_update *= 2;
+ pbdev->fmb.last_update |= UPDATE_U_BIT;
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update),
+ pbdev->fmb.last_update,
+ sizeof(pbdev->fmb.last_update))) {
+ return;
+ }
+
+ /* Update FMB sample count */
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, sample),
+ pbdev->fmb.sample++,
+ sizeof(pbdev->fmb.sample))) {
+ return;
+ }
+
+ /* Update FMB counters */
+ for (i = 0; i < ZPCI_FMB_CNT_MAX; i++) {
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, counter[i]),
+ pbdev->fmb.counter[i],
+ sizeof(pbdev->fmb.counter[0]))) {
+ return;
+ }
+ }
+
+ /* Clear U bit and update the time */
+ pbdev->fmb.last_update = time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ pbdev->fmb.last_update *= 2;
+ if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update),
+ pbdev->fmb.last_update,
+ sizeof(pbdev->fmb.last_update))) {
+ return;
+ }
+ timer_mod(pbdev->fmb_timer, t + DEFAULT_MUI);
+}
+
int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra)
{
@@ -1018,9 +1119,35 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
}
break;
- case ZPCI_MOD_FC_SET_MEASURE:
- pbdev->fmb_addr = ldq_p(&fib.fmb_addr);
+ case ZPCI_MOD_FC_SET_MEASURE: {
+ uint64_t fmb_addr = ldq_p(&fib.fmb_addr);
+
+ if (fmb_addr & FMBK_MASK) {
+ cc = ZPCI_PCI_LS_ERR;
+ s390_pci_generate_error_event(ERR_EVENT_FMBPRO, pbdev->fh,
+ pbdev->fid, fmb_addr, 0);
+ fmb_timer_free(pbdev);
+ break;
+ }
+
+ if (!fmb_addr) {
+ /* Stop updating FMB. */
+ fmb_timer_free(pbdev);
+ break;
+ }
+
+ if (!pbdev->fmb_timer) {
+ pbdev->fmb_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ fmb_update, pbdev);
+ } else if (timer_pending(pbdev->fmb_timer)) {
+ /* Remove pending timer to update FMB address. */
+ timer_del(pbdev->fmb_timer);
+ }
+ pbdev->fmb_addr = fmb_addr;
+ timer_mod(pbdev->fmb_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + DEFAULT_MUI);
break;
+ }
default:
s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra);
cc = ZPCI_PCI_LS_ERR;