On Tue, 4 Sep 2018 17:15:49 +0800 Yi Min Zhao <zyi...@linux.ibm.com> wrote:
> Common function measurement block is used to report counters of > successfully issued pcilg/stg/stb and rpcit instructions. This patch > introduces a new struct ZpciFmb and schedules a timer callback to > copy fmb to the guest memory at a interval time which is set to > 4s by default. While attemping to update fmb failed, an event error > would be generated. After pcilg/stg/stb and rpcit interception > handlers issue successfully, increase the related counter. The guest > could pass null address to switch off FMB and stop corresponding > timer. Hard to review without documentation, but some comments below. > > Signed-off-by: Yi Min Zhao <zyi...@linux.ibm.com> > Reviewed-by: Pierre Morel<pmo...@linux.ibm.com> > --- > hw/s390x/s390-pci-bus.c | 3 +- > hw/s390x/s390-pci-bus.h | 24 +++++++++++ > hw/s390x/s390-pci-inst.c | 105 > +++++++++++++++++++++++++++++++++++++++++++++-- > hw/s390x/s390-pci-inst.h | 1 + > 4 files changed, 129 insertions(+), 4 deletions(-) (...) > diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c > index 7b61367ee3..1ed5cb91d0 100644 > --- a/hw/s390x/s390-pci-inst.c > +++ b/hw/s390x/s390-pci-inst.c > @@ -456,6 +457,10 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t > r2, uintptr_t ra) > return 0; > } > > + if (pbdev->fmb_addr) { > + pbdev->fmb.ld_ops++; > + } As fmb is a part of the structure, just update it unconditionally? You'll only copy it to the guest if measurements are active anyway. > + > env->regs[r1] = data; > setcc(cpu, ZPCI_PCI_LS_OK); > return 0; (...) > @@ -889,6 +905,63 @@ 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)); Maybe move clearing the buffer to before you enable measurements instead? (Needed to make my suggestion above work correctly.) > +} > + > +static int fmb_do_update(S390PCIBusDevice *pbdev, uint8_t offset, int len) > +{ > + MemTxResult ret; > + > + ret = address_space_write(&address_space_memory, > + pbdev->fmb_addr + (uint64_t)offset, > + MEMTXATTRS_UNSPECIFIED, > + (uint8_t *)&pbdev->fmb + offset, > + len); > + if (ret) { > + s390_pci_generate_error_event(ERR_EVENT_FMBA, pbdev->fh, pbdev->fid, > + pbdev->fmb_addr, 0); > + fmb_timer_free(pbdev); So, failure to update the guest-provided area is supposed to disable measurements? > + } > + > + return ret; > +} > + > +static void fmb_update(void *opaque) > +{ > + S390PCIBusDevice *pbdev = opaque; > + int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); > + uint8_t offset = offsetof(ZpciFmb, last_update); > + > + /* Update U bit */ > + pbdev->fmb.last_update |= UPDATE_U_BIT; > + if (fmb_do_update(pbdev, offset, sizeof(uint64_t))) { > + return; > + } > + > + /* Update FMB counters */ > + pbdev->fmb.sample++; > + if (fmb_do_update(pbdev, 0, sizeof(ZpciFmb))) { > + return; > + } > + > + /* Clear U bit and update the time */ > + pbdev->fmb.last_update = time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); > + pbdev->fmb.last_update &= ~UPDATE_U_BIT; > + if (fmb_do_update(pbdev, offset, sizeof(uint64_t))) { > + 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 +1091,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; > + } > + > + pbdev->fmb_addr = fmb_addr; > + 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); > + } So, setting fmb_addr to another address will not reset the counters, but just cause the blocks to be stored into a different address? > + 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;