Message ID | 20181022090255.19671-1-zyimin@linux.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v2] s390x/pci: add common fmb | expand |
On 2018-10-22 10:02, Yi Min Zhao 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. > > Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com> > Reviewed-by: Pierre Morel <pmorel@linux.ibm.com> > --- [...] > +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); > + } > + > + 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); > +} Sorry for noticing this in v1 already, but is this code endianess-safe? I.e. can this also work with qemu-system-s390x running with TCG on a x86 host? I think you might have to use something like this here instead: pbdev->fmb.sample = cpu_to_be32(be32_to_cpu(pbdev->fmb.sample) + 1); etc. Thomas
在 2018/10/22 下午8:17, Thomas Huth 写道: > On 2018-10-22 10:02, Yi Min Zhao 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. >> >> Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com> >> Reviewed-by: Pierre Morel <pmorel@linux.ibm.com> >> --- > [...] >> +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); >> + } >> + >> + 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); >> +} > Sorry for noticing this in v1 already, but is this code endianess-safe? > I.e. can this also work with qemu-system-s390x running with TCG on a x86 > host? I think you might have to use something like this here instead: > > pbdev->fmb.sample = cpu_to_be32(be32_to_cpu(pbdev->fmb.sample) + 1); > > etc. > > Thomas > > Aha!!! Yes, I think you're right. Indeed, we should consider endianess.
On Mon, 22 Oct 2018 13:17:34 +0100 Thomas Huth <thuth@redhat.com> wrote: > On 2018-10-22 10:02, Yi Min Zhao 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. > > > > Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com> > > Reviewed-by: Pierre Morel <pmorel@linux.ibm.com> > > --- > [...] > > +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); > > + } > > + > > + 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); > > +} > > Sorry for noticing this in v1 already, but is this code endianess-safe? > I.e. can this also work with qemu-system-s390x running with TCG on a x86 > host? I think you might have to use something like this here instead: > > pbdev->fmb.sample = cpu_to_be32(be32_to_cpu(pbdev->fmb.sample) + 1); > > etc. Agreed, that may need some endianness handling. I would test this with tcg on a LE host, but how can I verify this? Yi Min, do you have some kind of test tooling you can share?
在 2018/10/24 上午5:25, Cornelia Huck 写道: > On Mon, 22 Oct 2018 13:17:34 +0100 > Thomas Huth <thuth@redhat.com> wrote: > >> On 2018-10-22 10:02, Yi Min Zhao 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. >>> >>> Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com> >>> Reviewed-by: Pierre Morel <pmorel@linux.ibm.com> >>> --- >> [...] >>> +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); >>> + } >>> + >>> + 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); >>> +} >> Sorry for noticing this in v1 already, but is this code endianess-safe? >> I.e. can this also work with qemu-system-s390x running with TCG on a x86 >> host? I think you might have to use something like this here instead: >> >> pbdev->fmb.sample = cpu_to_be32(be32_to_cpu(pbdev->fmb.sample) + 1); >> >> etc. > Agreed, that may need some endianness handling. > > I would test this with tcg on a LE host, but how can I verify this? Yi > Min, do you have some kind of test tooling you can share? > > There's no tool now. You could startup a guest. And then in the guest, install PCI driver and read FMB values from /sys/kernel/debug/pci/****/statistics. If endianness has error, I think the values must looks wrong. The right thing is that values increase from 0 and intervally.
On Wed, 24 Oct 2018 11:58:33 +0800 Yi Min Zhao <zyimin@linux.ibm.com> wrote: > 在 2018/10/24 上午5:25, Cornelia Huck 写道: > > On Mon, 22 Oct 2018 13:17:34 +0100 > > Thomas Huth <thuth@redhat.com> wrote: > > > >> On 2018-10-22 10:02, Yi Min Zhao 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. > >>> > >>> Signed-off-by: Yi Min Zhao <zyimin@linux.ibm.com> > >>> Reviewed-by: Pierre Morel <pmorel@linux.ibm.com> > >>> --- > >> [...] > >>> +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); > >>> + } > >>> + > >>> + 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); > >>> +} > >> Sorry for noticing this in v1 already, but is this code endianess-safe? > >> I.e. can this also work with qemu-system-s390x running with TCG on a x86 > >> host? I think you might have to use something like this here instead: > >> > >> pbdev->fmb.sample = cpu_to_be32(be32_to_cpu(pbdev->fmb.sample) + 1); > >> > >> etc. > > Agreed, that may need some endianness handling. > > > > I would test this with tcg on a LE host, but how can I verify this? Yi > > Min, do you have some kind of test tooling you can share? > > > > > There's no tool now. You could startup a guest. And then in the guest, > install > PCI driver and read FMB values from /sys/kernel/debug/pci/****/statistics. > > If endianness has error, I think the values must looks wrong. > The right thing is that values increase from 0 and intervally. > Thanks for pointing me to that file; when I run under tcg, the values indeed look like they have an endianness issue: Update interval: 4000 ms Samples: 637534208 Last update TOD: f4c01d0098000000 Load operations: 10520408729537478656 Store operations: 5980780305148018688 Store block operations: 0 Refresh operations: 0 Allocated pages: 0 Mapped pages: 0 Unmapped pages: 0 (virtio-net-pci device on a just-booted guest)
On 31/10/2018 11:49, Cornelia Huck wrote: > On Wed, 24 Oct 2018 11:58:33 +0800 > Yi Min Zhao <zyimin@linux.ibm.com> wrote: > >> 在 2018/10/24 上午5:25, Cornelia Huck 写道: >>> On Mon, 22 Oct 2018 13:17:34 +0100 >>> Thomas Huth <thuth@redhat.com> wrote: >>> ...snip... >> If endianness has error, I think the values must looks wrong. >> The right thing is that values increase from 0 and intervally. >> > > Thanks for pointing me to that file; when I run under tcg, the values > indeed look like they have an endianness issue: > > Update interval: 4000 ms > Samples: 637534208 > Last update TOD: f4c01d0098000000 > Load operations: 10520408729537478656 > Store operations: 5980780305148018688 > Store block operations: 0 > Refresh operations: 0 > Allocated pages: 0 > Mapped pages: 0 > Unmapped pages: 0 > > (virtio-net-pci device on a just-booted guest) > Hy Conny, I saw we lack a response to Thomas. Otherwise have you any remark? Regards, Pierre
On Fri, 30 Nov 2018 10:23:14 +0100 Pierre Morel <pmorel@linux.ibm.com> wrote: > On 31/10/2018 11:49, Cornelia Huck wrote: > > On Wed, 24 Oct 2018 11:58:33 +0800 > > Yi Min Zhao <zyimin@linux.ibm.com> wrote: > > > >> 在 2018/10/24 上午5:25, Cornelia Huck 写道: > >>> On Mon, 22 Oct 2018 13:17:34 +0100 > >>> Thomas Huth <thuth@redhat.com> wrote: > >>> > > ...snip... > > >> If endianness has error, I think the values must looks wrong. > >> The right thing is that values increase from 0 and intervally. > >> > > > > Thanks for pointing me to that file; when I run under tcg, the values > > indeed look like they have an endianness issue: > > > > Update interval: 4000 ms > > Samples: 637534208 > > Last update TOD: f4c01d0098000000 > > Load operations: 10520408729537478656 > > Store operations: 5980780305148018688 > > Store block operations: 0 > > Refresh operations: 0 > > Allocated pages: 0 > > Mapped pages: 0 > > Unmapped pages: 0 > > > > (virtio-net-pci device on a just-booted guest) > > > > Hy Conny, > > I saw we lack a response to Thomas. > Otherwise have you any remark? I don't remember anything beyond the endianess issue.
On 11/30/2018 04:27 AM, Cornelia Huck wrote: > On Fri, 30 Nov 2018 10:23:14 +0100 > Pierre Morel <pmorel@linux.ibm.com> wrote: > >> On 31/10/2018 11:49, Cornelia Huck wrote: >>> On Wed, 24 Oct 2018 11:58:33 +0800 >>> Yi Min Zhao <zyimin@linux.ibm.com> wrote: >>> >>>> 在 2018/10/24 上午5:25, Cornelia Huck 写道: >>>>> On Mon, 22 Oct 2018 13:17:34 +0100 >>>>> Thomas Huth <thuth@redhat.com> wrote: >>>>> >> >> ...snip... >> >>>> If endianness has error, I think the values must looks wrong. >>>> The right thing is that values increase from 0 and intervally. >>>> >>> >>> Thanks for pointing me to that file; when I run under tcg, the values >>> indeed look like they have an endianness issue: >>> >>> Update interval: 4000 ms >>> Samples: 637534208 >>> Last update TOD: f4c01d0098000000 >>> Load operations: 10520408729537478656 >>> Store operations: 5980780305148018688 >>> Store block operations: 0 >>> Refresh operations: 0 >>> Allocated pages: 0 >>> Mapped pages: 0 >>> Unmapped pages: 0 >>> >>> (virtio-net-pci device on a just-booted guest) >>> >> >> Hy Conny, >> >> I saw we lack a response to Thomas. >> Otherwise have you any remark? > > I don't remember anything beyond the endianess issue. > This patch looks sane to me (I've lost the parent email on my client, else I would've replied directly to that). I'm currently awaiting getting my system up-and-running to test this thoroughly. Shall we do one more round with the endianess addressed in the mean time?
On Wed, 12 Dec 2018 15:25:57 -0500 Collin Walling <walling@linux.ibm.com> wrote: > On 11/30/2018 04:27 AM, Cornelia Huck wrote: > > On Fri, 30 Nov 2018 10:23:14 +0100 > > Pierre Morel <pmorel@linux.ibm.com> wrote: > > > >> On 31/10/2018 11:49, Cornelia Huck wrote: > >>> On Wed, 24 Oct 2018 11:58:33 +0800 > >>> Yi Min Zhao <zyimin@linux.ibm.com> wrote: > >>> > >>>> 在 2018/10/24 上午5:25, Cornelia Huck 写道: > >>>>> On Mon, 22 Oct 2018 13:17:34 +0100 > >>>>> Thomas Huth <thuth@redhat.com> wrote: > >>>>> > >> > >> ...snip... > >> > >>>> If endianness has error, I think the values must looks wrong. > >>>> The right thing is that values increase from 0 and intervally. > >>>> > >>> > >>> Thanks for pointing me to that file; when I run under tcg, the values > >>> indeed look like they have an endianness issue: > >>> > >>> Update interval: 4000 ms > >>> Samples: 637534208 > >>> Last update TOD: f4c01d0098000000 > >>> Load operations: 10520408729537478656 > >>> Store operations: 5980780305148018688 > >>> Store block operations: 0 > >>> Refresh operations: 0 > >>> Allocated pages: 0 > >>> Mapped pages: 0 > >>> Unmapped pages: 0 > >>> > >>> (virtio-net-pci device on a just-booted guest) > >>> > >> > >> Hy Conny, > >> > >> I saw we lack a response to Thomas. > >> Otherwise have you any remark? > > > > I don't remember anything beyond the endianess issue. > > > > This patch looks sane to me (I've lost the parent email on my > client, else I would've replied directly to that). > > I'm currently awaiting getting my system up-and-running to test > this thoroughly. Shall we do one more round with the endianess > addressed in the mean time? Sure; I'll need to rely on your testing anyway (but I'll give it a whirl with virtio-pci).
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index e42e1b80d6..6cd23175cd 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -976,6 +976,7 @@ static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, bus = pci_get_bus(pci_dev); devfn = pci_dev->devfn; object_unparent(OBJECT(pci_dev)); + fmb_timer_free(pbdev); s390_pci_msix_free(pbdev); s390_pci_iommu_free(s, bus, devfn); pbdev->pdev = NULL; @@ -1147,7 +1148,7 @@ static void s390_pci_device_reset(DeviceState *dev) pci_dereg_ioat(pbdev->iommu); } - pbdev->fmb_addr = 0; + fmb_timer_free(pbdev); } static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 1f7f9b5814..bfbbaca26c 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -286,6 +286,29 @@ typedef struct S390PCIIOMMUTable { S390PCIIOMMU *iommu[PCI_SLOT_MAX]; } S390PCIIOMMUTable; +/* Function Measurement Block */ +#define DEFAULT_MUI 4000 +#define UPDATE_U_BIT 0x1ULL +#define FMBK_MASK 0xfULL + +typedef struct ZpciFmbFmt0 { + uint64_t dma_rbytes; + uint64_t dma_wbytes; +} ZpciFmbFmt0; + +typedef struct ZpciFmb { + uint8_t format; + uint8_t fmt_ind[3]; + uint32_t sample; + uint64_t last_update; + uint64_t ld_ops; + uint64_t st_ops; + uint64_t stb_ops; + uint64_t rpcit_ops; + ZpciFmbFmt0 fmt0; +} ZpciFmb; +QEMU_BUILD_BUG_MSG(offsetof(ZpciFmb, fmt0) != 48, "padding in ZpciFmb"); + struct S390PCIBusDevice { DeviceState qdev; PCIDevice *pdev; @@ -297,6 +320,8 @@ struct S390PCIBusDevice { uint32_t fid; bool fid_defined; uint64_t fmb_addr; + ZpciFmb fmb; + QEMUTimer *fmb_timer; uint8_t isc; uint16_t noi; uint16_t maxstbl; 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 @@ -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,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++; + } + env->regs[r1] = data; setcc(cpu, ZPCI_PCI_LS_OK); return 0; @@ -561,6 +566,10 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) return 0; } + if (pbdev->fmb_addr) { + pbdev->fmb.st_ops++; + } + setcc(cpu, ZPCI_PCI_LS_OK); return 0; } @@ -681,6 +690,9 @@ 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 { + if (pbdev->fmb_addr) { + pbdev->fmb.rpcit_ops++; + } setcc(cpu, ZPCI_PCI_LS_OK); } return 0; @@ -783,6 +795,10 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } } + if (pbdev->fmb_addr) { + pbdev->fmb.stb_ops++; + } + 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)); +} + +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); + } + + 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); + } + 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; diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index 91c3d61f2a..fa3bf8b5aa 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -303,6 +303,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uintptr_t ra); int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uintptr_t ra); +void fmb_timer_free(S390PCIBusDevice *pbdev); #define ZPCI_IO_BAR_MIN 0 #define ZPCI_IO_BAR_MAX 5