* [PATCH v8 01/16] s390x/pci: implement IOMMU replay
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-16 18:44 ` Farhan Ali
2026-09-11 15:21 ` [PATCH v8 02/16] s390x/pci: Create function to contain translation status check Konstantin Shkolnyy
` (14 subsequent siblings)
15 siblings, 1 reply; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
From: Matthew Rosato <mjrosato@linux.ibm.com>
There are a few scenarios where IOMMU replay can potentially be needed
for zPCI device, namely VFIO device reset scenarios where the guest
continues running and expects the contents of its IOMMU to be replayed
upon IOAT re-registration and migration scenarios where the destination
must reconstruct the IOMMU on the destination.
zPCI migration is not supported yet, but the IOMMU replay function is
implemented so that it can be called both from IOMMUMemoryRegionClass
now and migration post_load later.
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 68 +++++++++++++++++++++++++++++---
hw/s390x/s390-pci-inst.c | 4 +-
include/hw/s390x/s390-pci-inst.h | 1 +
3 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index eff980fdfe..10b417a779 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -593,14 +593,70 @@ err:
return ret;
}
-static void s390_pci_iommu_replay(IOMMUMemoryRegion *iommu,
+static void s390_pci_ioat_replay(S390PCIIOMMU *iommu)
+{
+ S390PCIBusDevice *pbdev = iommu->pbdev;
+ S390IOTLBEntry entry;
+ uint16_t error = 0;
+ uint32_t dma_avail;
+ hwaddr curr, end;
+
+ curr = iommu->pba;
+ end = iommu->pal;
+
+ if (iommu->dm_mr || !pbdev) {
+ /* If direct mapping is used, there are no guest tables to replay */
+ return;
+ }
+
+ if (iommu->dma_limit) {
+ dma_avail = iommu->dma_limit->avail;
+ } else {
+ dma_avail = 1;
+ }
+
+ while (curr < end) {
+ error = s390_guest_io_table_walk(iommu->g_iota, curr, &entry);
+ if (error) {
+ pbdev->state = ZPCI_FS_ERROR;
+ s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, curr,
+ 0);
+ error_report("Failure to walk table during iommu remap");
+ return;
+ }
+
+ curr += entry.len;
+ if (entry.perm != IOMMU_NONE) {
+ while (entry.iova < curr && entry.iova < end) {
+ if (dma_avail > 0) {
+ dma_avail = s390_pci_update_iotlb(iommu, &entry);
+ } else {
+ /*
+ * There is no reliable method to request the guest to
+ * release mappings other than in response to a RPCIT
+ * instruction; generate a permanent error condition and
+ * require the device to be completely re-initialized from
+ * the guest side.
+ */
+ pbdev->state = ZPCI_FS_ERROR;
+ s390_pci_generate_error_event(ERR_EVENT_PERMERR, pbdev->fh,
+ pbdev->fid, 0, 0);
+ error_report("DMA mappings exhausted: iommu remap failed");
+ return;
+ }
+ entry.iova += TARGET_PAGE_SIZE;
+ entry.translated_addr += TARGET_PAGE_SIZE;
+ }
+ }
+ }
+}
+
+static void s390_pci_iommu_replay(IOMMUMemoryRegion *mr,
IOMMUNotifier *notifier)
{
- /* It's impossible to plug a pci device on s390x that already has iommu
- * mappings which need to be replayed, that is due to the "one iommu per
- * zpci device" construct. But when we support migration of vfio-pci
- * devices in future, we need to revisit this.
- */
+ S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
+
+ s390_pci_ioat_replay(iommu);
}
static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus,
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index b7de23c7d2..37ca70dcf1 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -630,8 +630,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0;
}
-static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
- S390IOTLBEntry *entry)
+uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
+ S390IOTLBEntry *entry)
{
S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
IOMMUTLBEvent event = {
diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
index 5cb8da540b..c782990e3b 100644
--- a/include/hw/s390x/s390-pci-inst.h
+++ b/include/hw/s390x/s390-pci-inst.h
@@ -111,6 +111,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra);
void fmb_timer_free(S390PCIBusDevice *pbdev);
+uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry);
#define ZPCI_IO_BAR_MIN 0
#define ZPCI_IO_BAR_MAX 5
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v8 01/16] s390x/pci: implement IOMMU replay
2026-09-11 15:21 ` [PATCH v8 01/16] s390x/pci: implement IOMMU replay Konstantin Shkolnyy
@ 2026-09-16 18:44 ` Farhan Ali
2026-09-16 19:05 ` Matthew Rosato
0 siblings, 1 reply; 26+ messages in thread
From: Farhan Ali @ 2026-09-16 18:44 UTC (permalink / raw)
To: Konstantin Shkolnyy, mjrosato
Cc: farman, richard.henderson, iii, david, cohuck, pasic, borntraeger,
qemu-s390x, qemu-devel
On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
> From: Matthew Rosato <mjrosato@linux.ibm.com>
>
> There are a few scenarios where IOMMU replay can potentially be needed
> for zPCI device, namely VFIO device reset scenarios where the guest
> continues running and expects the contents of its IOMMU to be replayed
> upon IOAT re-registration and migration scenarios where the destination
> must reconstruct the IOMMU on the destination.
>
> zPCI migration is not supported yet, but the IOMMU replay function is
> implemented so that it can be called both from IOMMUMemoryRegionClass
> now and migration post_load later.
>
> Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
> Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
> ---
> hw/s390x/s390-pci-bus.c | 68 +++++++++++++++++++++++++++++---
> hw/s390x/s390-pci-inst.c | 4 +-
> include/hw/s390x/s390-pci-inst.h | 1 +
> 3 files changed, 65 insertions(+), 8 deletions(-)
>
> diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
> index eff980fdfe..10b417a779 100644
> --- a/hw/s390x/s390-pci-bus.c
> +++ b/hw/s390x/s390-pci-bus.c
> @@ -593,14 +593,70 @@ err:
> return ret;
> }
>
> -static void s390_pci_iommu_replay(IOMMUMemoryRegion *iommu,
> +static void s390_pci_ioat_replay(S390PCIIOMMU *iommu)
> +{
> + S390PCIBusDevice *pbdev = iommu->pbdev;
> + S390IOTLBEntry entry;
> + uint16_t error = 0;
> + uint32_t dma_avail;
> + hwaddr curr, end;
> +
> + curr = iommu->pba;
> + end = iommu->pal;
> +
> + if (iommu->dm_mr || !pbdev) {
> + /* If direct mapping is used, there are no guest tables to replay */
> + return;
> + }
> +
> + if (iommu->dma_limit) {
> + dma_avail = iommu->dma_limit->avail;
> + } else {
> + dma_avail = 1;
> + }
> +
> + while (curr < end) {
> + error = s390_guest_io_table_walk(iommu->g_iota, curr, &entry);
> + if (error) {
> + pbdev->state = ZPCI_FS_ERROR;
> + s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, curr,
> + 0);
> + error_report("Failure to walk table during iommu remap");
> + return;
> + }
> +
> + curr += entry.len;
> + if (entry.perm != IOMMU_NONE) {
> + while (entry.iova < curr && entry.iova < end) {
> + if (dma_avail > 0) {
> + dma_avail = s390_pci_update_iotlb(iommu, &entry);
> + } else {
> + /*
> + * There is no reliable method to request the guest to
> + * release mappings other than in response to a RPCIT
> + * instruction; generate a permanent error condition and
> + * require the device to be completely re-initialized from
> + * the guest side.
> + */
> + pbdev->state = ZPCI_FS_ERROR;
> + s390_pci_generate_error_event(ERR_EVENT_PERMERR, pbdev->fh,
> + pbdev->fid, 0, 0);
A permanent error will indicate to the guest that PCI device in
unusable, in that case would the guest drive a device re-initialization?
Would it be better to generate a ERR_EVENT_SERVAC to allow the guest to
attempt recovery and so drive the mappings again?
Thanks
Farhan
> + error_report("DMA mappings exhausted: iommu remap failed");
> + return;
> + }
> + entry.iova += TARGET_PAGE_SIZE;
> + entry.translated_addr += TARGET_PAGE_SIZE;
> + }
> + }
> + }
> +}
> +
> +static void s390_pci_iommu_replay(IOMMUMemoryRegion *mr,
> IOMMUNotifier *notifier)
> {
> - /* It's impossible to plug a pci device on s390x that already has iommu
> - * mappings which need to be replayed, that is due to the "one iommu per
> - * zpci device" construct. But when we support migration of vfio-pci
> - * devices in future, we need to revisit this.
> - */
> + S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
> +
> + s390_pci_ioat_replay(iommu);
> }
>
> static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus,
> diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
> index b7de23c7d2..37ca70dcf1 100644
> --- a/hw/s390x/s390-pci-inst.c
> +++ b/hw/s390x/s390-pci-inst.c
> @@ -630,8 +630,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
> return 0;
> }
>
> -static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
> - S390IOTLBEntry *entry)
> +uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
> + S390IOTLBEntry *entry)
> {
> S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
> IOMMUTLBEvent event = {
> diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
> index 5cb8da540b..c782990e3b 100644
> --- a/include/hw/s390x/s390-pci-inst.h
> +++ b/include/hw/s390x/s390-pci-inst.h
> @@ -111,6 +111,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
> int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
> uintptr_t ra);
> void fmb_timer_free(S390PCIBusDevice *pbdev);
> +uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry);
>
> #define ZPCI_IO_BAR_MIN 0
> #define ZPCI_IO_BAR_MAX 5
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v8 01/16] s390x/pci: implement IOMMU replay
2026-09-16 18:44 ` Farhan Ali
@ 2026-09-16 19:05 ` Matthew Rosato
2026-09-16 19:17 ` Farhan Ali
0 siblings, 1 reply; 26+ messages in thread
From: Matthew Rosato @ 2026-09-16 19:05 UTC (permalink / raw)
To: Farhan Ali, Konstantin Shkolnyy
Cc: farman, richard.henderson, iii, david, cohuck, pasic, borntraeger,
qemu-s390x, qemu-devel
>> + pbdev->state = ZPCI_FS_ERROR;
>> + s390_pci_generate_error_event(ERR_EVENT_PERMERR,
>> pbdev->fh,
>> + pbdev->fid, 0, 0);
>
> A permanent error will indicate to the guest that PCI device in
> unusable, in that case would the guest drive a device re-initialization?
> Would it be better to generate a ERR_EVENT_SERVAC to allow the guest to
> attempt recovery and so drive the mappings again?
>
The problem is that if we hit this scenario, we've exhausted the vfio
DMA limit for the device, so there is no way to create more mappings
without freeing some other ones up.
In the case of a RPCIT, we can ask the guest to try and free up all
stale/invalidated mappings to make room, but in this case
1) we have no mechanism to ask the guest to do that
2) we are in the middle replaying only the valid mappings, so there are
no invalid mappings to flush anyway -- so we're really in a permanent
error case here -- the number of vfio-allowed concurrent DMA mappings is
less than what our IOMMU wishes to replay. AFAICT this can only get
solved by forcing the guest to throw everything out and start over.
I think in reality, you would only hit this in a migration scenario with
a vfio-pci device (which we don't support with this series) where the
vfio DMA limit is lower on the target than it was on the host.
If you hit this doing IOMMU replay of a vfio-pci device without a
migration involved then the limit should be the same as it was before
replay and it should have been impossible to map more than the vfio DMA
limit (so if you managed it, a permanent error also seems reasonable
because there may be a deeper issue).
Would an 0x3a trigger a total wipe of the IOMMU contents that we were
trying to replay? If yes, it might be something to consider if/when we
support vfio-pci migration but I'm not sure it makes sense under the
current scenario(s) where we should never hit this path.
Thanks,
Matt
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v8 01/16] s390x/pci: implement IOMMU replay
2026-09-16 19:05 ` Matthew Rosato
@ 2026-09-16 19:17 ` Farhan Ali
0 siblings, 0 replies; 26+ messages in thread
From: Farhan Ali @ 2026-09-16 19:17 UTC (permalink / raw)
To: Matthew Rosato, Konstantin Shkolnyy
Cc: farman, richard.henderson, iii, david, cohuck, pasic, borntraeger,
qemu-s390x, qemu-devel
On 9/16/2026 12:05 PM, Matthew Rosato wrote:
>>> + pbdev->state = ZPCI_FS_ERROR;
>>> + s390_pci_generate_error_event(ERR_EVENT_PERMERR,
>>> pbdev->fh,
>>> + pbdev->fid, 0, 0);
>> A permanent error will indicate to the guest that PCI device in
>> unusable, in that case would the guest drive a device re-initialization?
>> Would it be better to generate a ERR_EVENT_SERVAC to allow the guest to
>> attempt recovery and so drive the mappings again?
>>
> The problem is that if we hit this scenario, we've exhausted the vfio
> DMA limit for the device, so there is no way to create more mappings
> without freeing some other ones up.
> In the case of a RPCIT, we can ask the guest to try and free up all
> stale/invalidated mappings to make room, but in this case
> 1) we have no mechanism to ask the guest to do that
> 2) we are in the middle replaying only the valid mappings, so there are
> no invalid mappings to flush anyway -- so we're really in a permanent
> error case here -- the number of vfio-allowed concurrent DMA mappings is
> less than what our IOMMU wishes to replay. AFAICT this can only get
> solved by forcing the guest to throw everything out and start over.
>
> I think in reality, you would only hit this in a migration scenario with
> a vfio-pci device (which we don't support with this series) where the
> vfio DMA limit is lower on the target than it was on the host.
> If you hit this doing IOMMU replay of a vfio-pci device without a
> migration involved then the limit should be the same as it was before
> replay and it should have been impossible to map more than the vfio DMA
> limit (so if you managed it, a permanent error also seems reasonable
> because there may be a deeper issue).
>
> Would an 0x3a trigger a total wipe of the IOMMU contents that we were
> trying to replay? If yes, it might be something to consider if/when we
> support vfio-pci migration but I'm not sure it makes sense under the
> current scenario(s) where we should never hit this path.
>
> Thanks,
> Matt
I think on 0x3a the guest will attempt a hot reset and so I think will
also re-initialize the IOMMU [1]. But as you said this only would apply
in the case of vfio-pci and may not be applicable to the virtio case. We
can re-visit this if/when we support vfio-pci migration.
[1] https://elixir.bootlin.com/linux/v7.2.5/source/arch/s390/pci/pci.c#L731
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v8 02/16] s390x/pci: Create function to contain translation status check
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 01/16] s390x/pci: implement IOMMU replay Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 03/16] s390x/pci: Move iommu_mr from S390PCIIOMMU to S390PCIBusDevice Konstantin Shkolnyy
` (13 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
Make it more clear what the bit means, and the new function will be called
from yet another place in the future.
Reviewed-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-inst.c | 7 ++++++-
include/hw/s390x/s390-pci-bus.h | 1 +
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 37ca70dcf1..00a847f493 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -1021,6 +1021,11 @@ int pci_dereg_irqs(S390PCIBusDevice *pbdev)
return 0;
}
+bool s390_pci_is_translation_enabled(uint64_t g_iota)
+{
+ return ((g_iota >> 11) & 0x1) != 0; /* "T" bit */
+}
+
static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
uintptr_t ra)
{
@@ -1029,7 +1034,7 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
uint64_t pal = ldq_be_p(&fib.pal);
uint64_t g_iota = ldq_be_p(&fib.iota);
uint8_t dt = (g_iota >> 2) & 0x7;
- uint8_t t = (g_iota >> 11) & 0x1;
+ bool t = s390_pci_is_translation_enabled(g_iota);
pba &= ~0xfff;
pal |= 0xfff;
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 9228523ce8..eb15cb8b2d 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -390,6 +390,7 @@ int pci_chsc_sei_nt2_get_event(void *res);
int pci_chsc_sei_nt2_have_event(void);
void s390_pci_sclp_configure(SCCB *sccb);
void s390_pci_sclp_deconfigure(SCCB *sccb);
+bool s390_pci_is_translation_enabled(uint64_t g_iota);
void s390_pci_iommu_enable(S390PCIIOMMU *iommu);
void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu);
void s390_pci_iommu_disable(S390PCIIOMMU *iommu);
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 03/16] s390x/pci: Move iommu_mr from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 01/16] s390x/pci: implement IOMMU replay Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 02/16] s390x/pci: Create function to contain translation status check Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 04/16] s390x/pci: Move dm_mr " Konstantin Shkolnyy
` (12 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 35 +++++++++++++++++---------------
hw/s390x/s390-pci-inst.c | 28 +++++++++++++------------
include/hw/s390x/s390-pci-bus.h | 6 +++---
include/hw/s390x/s390-pci-inst.h | 4 ++--
4 files changed, 39 insertions(+), 34 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 10b417a779..b6a03bcc6a 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -207,7 +207,7 @@ void s390_pci_sclp_deconfigure(SCCB *sccb)
pci_dereg_irqs(pbdev);
}
if (pbdev->iommu->enabled) {
- pci_dereg_ioat(pbdev->iommu);
+ pci_dereg_ioat(pbdev);
}
pbdev->state = ZPCI_FS_STANDBY;
rc = SCLP_RC_NORMAL_COMPLETION;
@@ -539,7 +539,8 @@ uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr,
static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
IOMMUAccessFlags flag, int iommu_idx)
{
- S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
+ S390PCIBusDevice *pbdev = container_of(mr, S390PCIBusDevice, iommu_mr);
+ S390PCIIOMMU *iommu = pbdev->iommu;
S390IOTLBEntry *entry;
uint64_t iova = addr & TARGET_PAGE_MASK;
uint16_t error = 0;
@@ -593,18 +594,18 @@ err:
return ret;
}
-static void s390_pci_ioat_replay(S390PCIIOMMU *iommu)
+static void s390_pci_ioat_replay(S390PCIBusDevice *pbdev)
{
- S390PCIBusDevice *pbdev = iommu->pbdev;
S390IOTLBEntry entry;
uint16_t error = 0;
uint32_t dma_avail;
hwaddr curr, end;
+ S390PCIIOMMU *iommu = pbdev->iommu;
curr = iommu->pba;
end = iommu->pal;
- if (iommu->dm_mr || !pbdev) {
+ if (iommu->dm_mr) {
/* If direct mapping is used, there are no guest tables to replay */
return;
}
@@ -629,7 +630,7 @@ static void s390_pci_ioat_replay(S390PCIIOMMU *iommu)
if (entry.perm != IOMMU_NONE) {
while (entry.iova < curr && entry.iova < end) {
if (dma_avail > 0) {
- dma_avail = s390_pci_update_iotlb(iommu, &entry);
+ dma_avail = s390_pci_update_iotlb(pbdev, &entry);
} else {
/*
* There is no reliable method to request the guest to
@@ -654,9 +655,9 @@ static void s390_pci_ioat_replay(S390PCIIOMMU *iommu)
static void s390_pci_iommu_replay(IOMMUMemoryRegion *mr,
IOMMUNotifier *notifier)
{
- S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr);
+ S390PCIBusDevice *pbdev = container_of(mr, S390PCIBusDevice, iommu_mr);
- s390_pci_ioat_replay(iommu);
+ s390_pci_ioat_replay(pbdev);
}
static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus,
@@ -778,19 +779,20 @@ static const MemoryRegionOps s390_msi_ctrl_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
-void s390_pci_iommu_enable(S390PCIIOMMU *iommu)
+void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
{
+ S390PCIIOMMU *iommu = pbdev->iommu;
/*
* The iommu region is initialized against a 0-mapped address space,
* so the smallest IOMMU region we can define runs from 0 to the end
* of the PCI address space.
*/
char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid);
- memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr),
+ memory_region_init_iommu(&pbdev->iommu_mr, sizeof(pbdev->iommu_mr),
TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
name, iommu->pal + 1);
iommu->enabled = true;
- memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr));
+ memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&pbdev->iommu_mr));
g_free(name);
}
@@ -816,8 +818,9 @@ void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu)
iommu->dm_mr);
}
-void s390_pci_iommu_disable(S390PCIIOMMU *iommu)
+void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
{
+ S390PCIIOMMU *iommu = pbdev->iommu;
iommu->enabled = false;
g_hash_table_remove_all(iommu->iotlb);
if (iommu->dm_mr) {
@@ -827,8 +830,8 @@ void s390_pci_iommu_disable(S390PCIIOMMU *iommu)
iommu->dm_mr = NULL;
} else {
memory_region_del_subregion(&iommu->mr,
- MEMORY_REGION(&iommu->iommu_mr));
- object_unparent(OBJECT(&iommu->iommu_mr));
+ MEMORY_REGION(&pbdev->iommu_mr));
+ object_unparent(OBJECT(&pbdev->iommu_mr));
}
}
@@ -1427,7 +1430,7 @@ static void s390_pcihost_reset(DeviceState *dev)
pci_dereg_irqs(pbdev);
}
if (pbdev->iommu->enabled) {
- pci_dereg_ioat(pbdev->iommu);
+ pci_dereg_ioat(pbdev);
}
pbdev->state = ZPCI_FS_STANDBY;
s390_pci_perform_unplug(pbdev);
@@ -1568,7 +1571,7 @@ static void s390_pci_device_reset(DeviceState *dev)
pci_dereg_irqs(pbdev);
}
if (pbdev->iommu->enabled) {
- pci_dereg_ioat(pbdev->iommu);
+ pci_dereg_ioat(pbdev);
}
fmb_timer_free(pbdev);
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 00a847f493..0772f04cc2 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -630,9 +630,10 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
return 0;
}
-uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
+uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
S390IOTLBEntry *entry)
{
+ S390PCIIOMMU *iommu = pbdev->iommu;
S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
IOMMUTLBEvent event = {
.type = entry->perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP,
@@ -662,7 +663,7 @@ uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.perm = IOMMU_NONE;
- memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
+ memory_region_notify_iommu(&pbdev->iommu_mr, 0, event);
event.type = IOMMU_NOTIFIER_MAP;
event.entry.perm = entry->perm;
}
@@ -680,13 +681,13 @@ uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
* All associated iotlb entries have already been cleared, trigger the
* unmaps.
*/
- memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
+ memory_region_notify_iommu(&pbdev->iommu_mr, 0, event);
out:
return iommu->dma_limit ? iommu->dma_limit->avail : 1;
}
-static void s390_pci_batch_unmap(S390PCIIOMMU *iommu, uint64_t iova,
+static void s390_pci_batch_unmap(S390PCIBusDevice *pbdev, uint64_t iova,
uint64_t len)
{
uint64_t remain = len, start = iova, end = start + len - 1, mask, size;
@@ -704,7 +705,7 @@ static void s390_pci_batch_unmap(S390PCIIOMMU *iommu, uint64_t iova,
size = mask + 1;
event.entry.iova = start;
event.entry.addr_mask = mask;
- memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
+ memory_region_notify_iommu(&pbdev->iommu_mr, 0, event);
start += size;
remain -= size;
}
@@ -801,14 +802,14 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
coalesce += entry.len;
} else if (coalesce > 0) {
/* Unleash the coalesced unmap before processing a new map */
- s390_pci_batch_unmap(iommu, iova, coalesce);
+ s390_pci_batch_unmap(pbdev, iova, coalesce);
coalesce = 0;
}
start += entry.len;
while (entry.iova < start && entry.iova < end) {
if (dma_avail > 0 || entry.perm == IOMMU_NONE) {
- dma_avail = s390_pci_update_iotlb(iommu, &entry);
+ dma_avail = s390_pci_update_iotlb(pbdev, &entry);
entry.iova += TARGET_PAGE_SIZE;
entry.translated_addr += TARGET_PAGE_SIZE;
} else {
@@ -824,7 +825,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
}
if (coalesce) {
/* Unleash the coalesced unmap before finishing rpcit */
- s390_pci_batch_unmap(iommu, iova, coalesce);
+ s390_pci_batch_unmap(pbdev, iova, coalesce);
coalesce = 0;
}
if (again && dma_avail > 0)
@@ -1059,7 +1060,7 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
iommu->g_iota = g_iota;
if (t) {
- s390_pci_iommu_enable(iommu);
+ s390_pci_iommu_enable(pbdev);
} else {
s390_pci_iommu_direct_map_enable(iommu);
}
@@ -1067,9 +1068,10 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
return 0;
}
-void pci_dereg_ioat(S390PCIIOMMU *iommu)
+void pci_dereg_ioat(S390PCIBusDevice *pbdev)
{
- s390_pci_iommu_disable(iommu);
+ S390PCIIOMMU *iommu = pbdev->iommu;
+ s390_pci_iommu_disable(pbdev);
iommu->pba = 0;
iommu->pal = 0;
iommu->g_iota = 0;
@@ -1293,7 +1295,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
} else {
- pci_dereg_ioat(pbdev->iommu);
+ pci_dereg_ioat(pbdev);
}
break;
case ZPCI_MOD_FC_REREG_IOAT:
@@ -1304,7 +1306,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
} else {
- pci_dereg_ioat(pbdev->iommu);
+ pci_dereg_ioat(pbdev);
if (reg_ioat(env, pbdev, fib, ra)) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_INSUF_RES);
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index eb15cb8b2d..a71f562dfc 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -276,7 +276,6 @@ struct S390PCIIOMMU {
S390PCIBusDevice *pbdev;
AddressSpace as;
MemoryRegion mr;
- IOMMUMemoryRegion iommu_mr;
MemoryRegion *dm_mr;
bool enabled;
uint64_t g_iota;
@@ -354,6 +353,7 @@ struct S390PCIBusDevice {
S390MsixInfo msix;
AdapterRoutes routes;
S390PCIIOMMU *iommu;
+ IOMMUMemoryRegion iommu_mr;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
IndAddr *indicator;
@@ -391,9 +391,9 @@ int pci_chsc_sei_nt2_have_event(void);
void s390_pci_sclp_configure(SCCB *sccb);
void s390_pci_sclp_deconfigure(SCCB *sccb);
bool s390_pci_is_translation_enabled(uint64_t g_iota);
-void s390_pci_iommu_enable(S390PCIIOMMU *iommu);
+void s390_pci_iommu_enable(S390PCIBusDevice *pbdev);
void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu);
-void s390_pci_iommu_disable(S390PCIIOMMU *iommu);
+void s390_pci_iommu_disable(S390PCIBusDevice *pbdev);
void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
uint64_t faddr, uint32_t e);
uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr,
diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
index c782990e3b..38268c256e 100644
--- a/include/hw/s390x/s390-pci-inst.h
+++ b/include/hw/s390x/s390-pci-inst.h
@@ -99,7 +99,7 @@ typedef struct ZpciFib {
} QEMU_PACKED ZpciFib;
int pci_dereg_irqs(S390PCIBusDevice *pbdev);
-void pci_dereg_ioat(S390PCIIOMMU *iommu);
+void pci_dereg_ioat(S390PCIBusDevice *pbdev);
int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra);
int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra);
int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra);
@@ -111,7 +111,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra);
void fmb_timer_free(S390PCIBusDevice *pbdev);
-uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry);
+uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev, S390IOTLBEntry *entry);
#define ZPCI_IO_BAR_MIN 0
#define ZPCI_IO_BAR_MAX 5
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 04/16] s390x/pci: Move dm_mr from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (2 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 03/16] s390x/pci: Move iommu_mr from S390PCIIOMMU to S390PCIBusDevice Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 05/16] s390x/pci: Move iotlb " Konstantin Shkolnyy
` (11 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify to simplify S390PCIIOMMU towards a structure that
contains only the IOMMU container information needed by the PCI layer for
a given slot.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 21 +++++++++++----------
hw/s390x/s390-pci-inst.c | 2 +-
include/hw/s390x/s390-pci-bus.h | 4 ++--
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index b6a03bcc6a..e1b47f3914 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -605,7 +605,7 @@ static void s390_pci_ioat_replay(S390PCIBusDevice *pbdev)
curr = iommu->pba;
end = iommu->pal;
- if (iommu->dm_mr) {
+ if (pbdev->dm_mr) {
/* If direct mapping is used, there are no guest tables to replay */
return;
}
@@ -796,8 +796,9 @@ void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
g_free(name);
}
-void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu)
+void s390_pci_iommu_direct_map_enable(S390PCIBusDevice *pbdev)
{
+ S390PCIIOMMU *iommu = pbdev->iommu;
MachineState *ms = MACHINE(qdev_get_machine());
S390CcwMachineState *s390ms = S390_CCW_MACHINE(ms);
@@ -809,13 +810,13 @@ void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu)
g_autofree char *name = g_strdup_printf("iommu-dm-s390-%04x",
iommu->pbdev->uid);
- iommu->dm_mr = g_malloc0(sizeof(*iommu->dm_mr));
- memory_region_init_alias(iommu->dm_mr, OBJECT(&iommu->mr), name,
+ pbdev->dm_mr = g_malloc0(sizeof(*pbdev->dm_mr));
+ memory_region_init_alias(pbdev->dm_mr, OBJECT(&iommu->mr), name,
get_system_memory(), 0,
s390_get_memory_limit(s390ms));
iommu->enabled = true;
memory_region_add_subregion(&iommu->mr, iommu->pbdev->zpci_fn.sdma,
- iommu->dm_mr);
+ pbdev->dm_mr);
}
void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
@@ -823,11 +824,11 @@ void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
S390PCIIOMMU *iommu = pbdev->iommu;
iommu->enabled = false;
g_hash_table_remove_all(iommu->iotlb);
- if (iommu->dm_mr) {
- memory_region_del_subregion(&iommu->mr, iommu->dm_mr);
- object_unparent(OBJECT(iommu->dm_mr));
- g_free(iommu->dm_mr);
- iommu->dm_mr = NULL;
+ if (pbdev->dm_mr) {
+ memory_region_del_subregion(&iommu->mr, pbdev->dm_mr);
+ object_unparent(OBJECT(pbdev->dm_mr));
+ g_free(pbdev->dm_mr);
+ pbdev->dm_mr = NULL;
} else {
memory_region_del_subregion(&iommu->mr,
MEMORY_REGION(&pbdev->iommu_mr));
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 0772f04cc2..582e93a741 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -1062,7 +1062,7 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
if (t) {
s390_pci_iommu_enable(pbdev);
} else {
- s390_pci_iommu_direct_map_enable(iommu);
+ s390_pci_iommu_direct_map_enable(pbdev);
}
return 0;
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index a71f562dfc..f310672584 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -276,7 +276,6 @@ struct S390PCIIOMMU {
S390PCIBusDevice *pbdev;
AddressSpace as;
MemoryRegion mr;
- MemoryRegion *dm_mr;
bool enabled;
uint64_t g_iota;
uint64_t pba;
@@ -354,6 +353,7 @@ struct S390PCIBusDevice {
AdapterRoutes routes;
S390PCIIOMMU *iommu;
IOMMUMemoryRegion iommu_mr;
+ MemoryRegion *dm_mr;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
IndAddr *indicator;
@@ -392,7 +392,7 @@ void s390_pci_sclp_configure(SCCB *sccb);
void s390_pci_sclp_deconfigure(SCCB *sccb);
bool s390_pci_is_translation_enabled(uint64_t g_iota);
void s390_pci_iommu_enable(S390PCIBusDevice *pbdev);
-void s390_pci_iommu_direct_map_enable(S390PCIIOMMU *iommu);
+void s390_pci_iommu_direct_map_enable(S390PCIBusDevice *pbdev);
void s390_pci_iommu_disable(S390PCIBusDevice *pbdev);
void s390_pci_generate_error_event(uint16_t pec, uint32_t fh, uint32_t fid,
uint64_t faddr, uint32_t e);
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 05/16] s390x/pci: Move iotlb from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (3 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 04/16] s390x/pci: Move dm_mr " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 06/16] s390x/pci: Remove a ptr to S390PCIBusDevice from S390PCIIOMMU Konstantin Shkolnyy
` (10 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 10 +++++-----
hw/s390x/s390-pci-inst.c | 6 +++---
include/hw/s390x/s390-pci-bus.h | 2 +-
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index e1b47f3914..7c1aa260be 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -570,7 +570,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
goto err;
}
- entry = g_hash_table_lookup(iommu->iotlb, &iova);
+ entry = g_hash_table_lookup(pbdev->iotlb, &iova);
if (entry) {
ret.iova = entry->iova;
ret.translated_addr = entry->translated_addr;
@@ -687,8 +687,6 @@ static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus,
PCI_FUNC(devfn));
memory_region_init(&iommu->mr, OBJECT(iommu), mr_name, UINT64_MAX);
address_space_init(&iommu->as, &iommu->mr, as_name);
- iommu->iotlb = g_hash_table_new_full(g_int64_hash, g_int64_equal,
- NULL, g_free);
table->iommu[PCI_SLOT(devfn)] = iommu;
g_free(mr_name);
@@ -823,7 +821,7 @@ void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
{
S390PCIIOMMU *iommu = pbdev->iommu;
iommu->enabled = false;
- g_hash_table_remove_all(iommu->iotlb);
+ g_hash_table_remove_all(pbdev->iotlb);
if (pbdev->dm_mr) {
memory_region_del_subregion(&iommu->mr, pbdev->dm_mr);
object_unparent(OBJECT(pbdev->dm_mr));
@@ -847,7 +845,6 @@ static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn)
}
table->iommu[PCI_SLOT(devfn)] = NULL;
- g_hash_table_destroy(iommu->iotlb);
/*
* An attached PCI device may have memory listeners, eg. VFIO PCI.
* The associated subregion will already have been unmapped in
@@ -1269,6 +1266,8 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
/* the allocated idx is actually getting used */
s->next_idx = (pbdev->idx + 1) & FH_MASK_INDEX;
pbdev->fh = pbdev->idx;
+ pbdev->iotlb = g_hash_table_new_full(g_int64_hash, g_int64_equal,
+ NULL, g_free);
QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link);
g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
} else {
@@ -1311,6 +1310,7 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
if (pbdev->iommu && pbdev->iommu->dma_limit) {
s390_pci_end_dma_count(s, pbdev->iommu->dma_limit);
}
+ g_hash_table_destroy(pbdev->iotlb);
qdev_unrealize(dev);
}
}
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 582e93a741..f648afb401 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -634,7 +634,7 @@ uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
S390IOTLBEntry *entry)
{
S390PCIIOMMU *iommu = pbdev->iommu;
- S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
+ S390IOTLBEntry *cache = g_hash_table_lookup(pbdev->iotlb, &entry->iova);
IOMMUTLBEvent event = {
.type = entry->perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP,
.entry = {
@@ -650,7 +650,7 @@ uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
if (!cache) {
goto out;
}
- g_hash_table_remove(iommu->iotlb, &entry->iova);
+ g_hash_table_remove(pbdev->iotlb, &entry->iova);
inc_dma_avail(iommu);
/* Don't notify the iommu yet, maybe we can bundle contiguous unmaps */
goto out;
@@ -673,7 +673,7 @@ uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
cache->translated_addr = entry->translated_addr;
cache->len = TARGET_PAGE_SIZE;
cache->perm = entry->perm;
- g_hash_table_replace(iommu->iotlb, &cache->iova, cache);
+ g_hash_table_replace(pbdev->iotlb, &cache->iova, cache);
dec_dma_avail(iommu);
}
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index f310672584..d9bafd6859 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -281,7 +281,6 @@ struct S390PCIIOMMU {
uint64_t pba;
uint64_t pal;
uint64_t max_dma_limit;
- GHashTable *iotlb;
S390PCIDMACount *dma_limit;
};
@@ -354,6 +353,7 @@ struct S390PCIBusDevice {
S390PCIIOMMU *iommu;
IOMMUMemoryRegion iommu_mr;
MemoryRegion *dm_mr;
+ GHashTable *iotlb;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
IndAddr *indicator;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 06/16] s390x/pci: Remove a ptr to S390PCIBusDevice from S390PCIIOMMU
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (4 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 05/16] s390x/pci: Move iotlb " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 07/16] s390x/pci: Move/rename enabled from S390PCIIOMMU to S390PCIBusDevice Konstantin Shkolnyy
` (9 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This pointer is no longer used, after fields were moved from S390PCIIOMMU
to S390PCIBusDevice.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 15 +++++++--------
include/hw/s390x/s390-pci-bus.h | 1 -
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 7c1aa260be..10e0c3324a 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -552,7 +552,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
.perm = IOMMU_NONE,
};
- switch (iommu->pbdev->state) {
+ switch (pbdev->state) {
case ZPCI_FS_ENABLED:
case ZPCI_FS_BLOCKED:
if (!iommu->enabled) {
@@ -587,9 +587,9 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
}
err:
if (error) {
- iommu->pbdev->state = ZPCI_FS_ERROR;
- s390_pci_generate_error_event(error, iommu->pbdev->fh,
- iommu->pbdev->fid, addr, 0);
+ pbdev->state = ZPCI_FS_ERROR;
+ s390_pci_generate_error_event(error, pbdev->fh,
+ pbdev->fid, addr, 0);
}
return ret;
}
@@ -785,7 +785,7 @@ void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
* so the smallest IOMMU region we can define runs from 0 to the end
* of the PCI address space.
*/
- char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid);
+ char *name = g_strdup_printf("iommu-s390-%04x", pbdev->uid);
memory_region_init_iommu(&pbdev->iommu_mr, sizeof(pbdev->iommu_mr),
TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
name, iommu->pal + 1);
@@ -806,14 +806,14 @@ void s390_pci_iommu_direct_map_enable(S390PCIBusDevice *pbdev)
* IOVA X + SDMA. VFIO will handle pinning via its memory listener.
*/
g_autofree char *name = g_strdup_printf("iommu-dm-s390-%04x",
- iommu->pbdev->uid);
+ pbdev->uid);
pbdev->dm_mr = g_malloc0(sizeof(*pbdev->dm_mr));
memory_region_init_alias(pbdev->dm_mr, OBJECT(&iommu->mr), name,
get_system_memory(), 0,
s390_get_memory_limit(s390ms));
iommu->enabled = true;
- memory_region_add_subregion(&iommu->mr, iommu->pbdev->zpci_fn.sdma,
+ memory_region_add_subregion(&iommu->mr, pbdev->zpci_fn.sdma,
pbdev->dm_mr);
}
@@ -1205,7 +1205,6 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
pbdev->pdev = pdev;
pbdev->iommu = s390_pci_get_iommu(s, pci_get_bus(pdev), pdev->devfn);
- pbdev->iommu->pbdev = pbdev;
pbdev->state = ZPCI_FS_DISABLED;
set_pbdev_info(pbdev);
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index d9bafd6859..fcaaf19512 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -273,7 +273,6 @@ typedef struct S390PCIDMACount {
struct S390PCIIOMMU {
Object parent_obj;
- S390PCIBusDevice *pbdev;
AddressSpace as;
MemoryRegion mr;
bool enabled;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 07/16] s390x/pci: Move/rename enabled from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (5 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 06/16] s390x/pci: Remove a ptr to S390PCIBusDevice from S390PCIIOMMU Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 08/16] s390x/pci: Move dma_limit " Konstantin Shkolnyy
` (8 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the IOMMU
container information needed by the PCI layer for a given slot.
This also allows to save/restore this field during migration.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 14 +++++++-------
hw/s390x/s390-pci-inst.c | 8 ++++----
include/hw/s390x/s390-pci-bus.h | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 10e0c3324a..ee05447084 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -206,7 +206,7 @@ void s390_pci_sclp_deconfigure(SCCB *sccb)
} else if (pbdev->summary_ind) {
pci_dereg_irqs(pbdev);
}
- if (pbdev->iommu->enabled) {
+ if (pbdev->iommu_enabled) {
pci_dereg_ioat(pbdev);
}
pbdev->state = ZPCI_FS_STANDBY;
@@ -555,7 +555,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
switch (pbdev->state) {
case ZPCI_FS_ENABLED:
case ZPCI_FS_BLOCKED:
- if (!iommu->enabled) {
+ if (!pbdev->iommu_enabled) {
return ret;
}
break;
@@ -789,7 +789,7 @@ void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
memory_region_init_iommu(&pbdev->iommu_mr, sizeof(pbdev->iommu_mr),
TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
name, iommu->pal + 1);
- iommu->enabled = true;
+ pbdev->iommu_enabled = true;
memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&pbdev->iommu_mr));
g_free(name);
}
@@ -812,7 +812,7 @@ void s390_pci_iommu_direct_map_enable(S390PCIBusDevice *pbdev)
memory_region_init_alias(pbdev->dm_mr, OBJECT(&iommu->mr), name,
get_system_memory(), 0,
s390_get_memory_limit(s390ms));
- iommu->enabled = true;
+ pbdev->iommu_enabled = true;
memory_region_add_subregion(&iommu->mr, pbdev->zpci_fn.sdma,
pbdev->dm_mr);
}
@@ -820,7 +820,7 @@ void s390_pci_iommu_direct_map_enable(S390PCIBusDevice *pbdev)
void s390_pci_iommu_disable(S390PCIBusDevice *pbdev)
{
S390PCIIOMMU *iommu = pbdev->iommu;
- iommu->enabled = false;
+ pbdev->iommu_enabled = false;
g_hash_table_remove_all(pbdev->iotlb);
if (pbdev->dm_mr) {
memory_region_del_subregion(&iommu->mr, pbdev->dm_mr);
@@ -1429,7 +1429,7 @@ static void s390_pcihost_reset(DeviceState *dev)
} else if (pbdev->summary_ind) {
pci_dereg_irqs(pbdev);
}
- if (pbdev->iommu->enabled) {
+ if (pbdev->iommu_enabled) {
pci_dereg_ioat(pbdev);
}
pbdev->state = ZPCI_FS_STANDBY;
@@ -1570,7 +1570,7 @@ static void s390_pci_device_reset(DeviceState *dev)
} else if (pbdev->summary_ind) {
pci_dereg_irqs(pbdev);
}
- if (pbdev->iommu->enabled) {
+ if (pbdev->iommu_enabled) {
pci_dereg_ioat(pbdev);
}
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index f648afb401..7081be4619 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -1279,7 +1279,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
if (dmaas != 0) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL);
- } else if (pbdev->iommu->enabled) {
+ } else if (pbdev->iommu_enabled) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
} else if (reg_ioat(env, pbdev, fib, ra)) {
@@ -1291,7 +1291,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
if (dmaas != 0) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL);
- } else if (!pbdev->iommu->enabled) {
+ } else if (!pbdev->iommu_enabled) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
} else {
@@ -1302,7 +1302,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
if (dmaas != 0) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_DMAAS_INVAL);
- } else if (!pbdev->iommu->enabled) {
+ } else if (!pbdev->iommu_enabled) {
cc = ZPCI_PCI_LS_ERR;
s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE);
} else {
@@ -1432,7 +1432,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
/* fallthrough */
case ZPCI_FS_ENABLED:
fib.fc |= 0x80;
- if (pbdev->iommu->enabled) {
+ if (pbdev->iommu_enabled) {
fib.fc |= 0x10;
}
if (!(fh & FH_MASK_ENABLE)) {
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index fcaaf19512..24f6e60786 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -275,7 +275,6 @@ struct S390PCIIOMMU {
Object parent_obj;
AddressSpace as;
MemoryRegion mr;
- bool enabled;
uint64_t g_iota;
uint64_t pba;
uint64_t pal;
@@ -350,6 +349,7 @@ struct S390PCIBusDevice {
S390MsixInfo msix;
AdapterRoutes routes;
S390PCIIOMMU *iommu;
+ bool iommu_enabled;
IOMMUMemoryRegion iommu_mr;
MemoryRegion *dm_mr;
GHashTable *iotlb;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 08/16] s390x/pci: Move dma_limit from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (6 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 07/16] s390x/pci: Move/rename enabled from S390PCIIOMMU to S390PCIBusDevice Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 09/16] s390x/pci: Move g_iota " Konstantin Shkolnyy
` (7 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 10 +++++-----
hw/s390x/s390-pci-inst.c | 23 +++++++++++------------
include/hw/s390x/s390-pci-bus.h | 2 +-
3 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index ee05447084..ad4f527430 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -610,8 +610,8 @@ static void s390_pci_ioat_replay(S390PCIBusDevice *pbdev)
return;
}
- if (iommu->dma_limit) {
- dma_avail = iommu->dma_limit->avail;
+ if (pbdev->dma_limit) {
+ dma_avail = pbdev->dma_limit->avail;
} else {
dma_avail = 1;
}
@@ -1228,7 +1228,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
pbdev->forwarding_assist = false;
}
}
- pbdev->iommu->dma_limit = s390_pci_start_dma_count(s, pbdev);
+ pbdev->dma_limit = s390_pci_start_dma_count(s, pbdev);
/* Fill in CLP information passed via the vfio region */
s390_pci_get_clp_info(pbdev);
if (!pbdev->interp) {
@@ -1306,8 +1306,8 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
pbdev->fid = 0;
QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
g_hash_table_remove(s->zpci_table, &pbdev->idx);
- if (pbdev->iommu && pbdev->iommu->dma_limit) {
- s390_pci_end_dma_count(s, pbdev->iommu->dma_limit);
+ if (pbdev->dma_limit) {
+ s390_pci_end_dma_count(s, pbdev->dma_limit);
}
g_hash_table_destroy(pbdev->iotlb);
qdev_unrealize(dev);
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 7081be4619..dc52da41b5 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -28,17 +28,17 @@
#include "trace.h"
-static inline void inc_dma_avail(S390PCIIOMMU *iommu)
+static inline void inc_dma_avail(S390PCIBusDevice *pbdev)
{
- if (iommu->dma_limit) {
- iommu->dma_limit->avail++;
+ if (pbdev->dma_limit) {
+ pbdev->dma_limit->avail++;
}
}
-static inline void dec_dma_avail(S390PCIIOMMU *iommu)
+static inline void dec_dma_avail(S390PCIBusDevice *pbdev)
{
- if (iommu->dma_limit) {
- iommu->dma_limit->avail--;
+ if (pbdev->dma_limit) {
+ pbdev->dma_limit->avail--;
}
}
@@ -633,7 +633,6 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
S390IOTLBEntry *entry)
{
- S390PCIIOMMU *iommu = pbdev->iommu;
S390IOTLBEntry *cache = g_hash_table_lookup(pbdev->iotlb, &entry->iova);
IOMMUTLBEvent event = {
.type = entry->perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP,
@@ -651,7 +650,7 @@ uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
goto out;
}
g_hash_table_remove(pbdev->iotlb, &entry->iova);
- inc_dma_avail(iommu);
+ inc_dma_avail(pbdev);
/* Don't notify the iommu yet, maybe we can bundle contiguous unmaps */
goto out;
} else {
@@ -674,7 +673,7 @@ uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
cache->len = TARGET_PAGE_SIZE;
cache->perm = entry->perm;
g_hash_table_replace(pbdev->iotlb, &cache->iova, cache);
- dec_dma_avail(iommu);
+ dec_dma_avail(pbdev);
}
/*
@@ -684,7 +683,7 @@ uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev,
memory_region_notify_iommu(&pbdev->iommu_mr, 0, event);
out:
- return iommu->dma_limit ? iommu->dma_limit->avail : 1;
+ return pbdev->dma_limit ? pbdev->dma_limit->avail : 1;
}
static void s390_pci_batch_unmap(S390PCIBusDevice *pbdev, uint64_t iova,
@@ -761,8 +760,8 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
}
iommu = pbdev->iommu;
- if (iommu->dma_limit) {
- dma_avail = iommu->dma_limit->avail;
+ if (pbdev->dma_limit) {
+ dma_avail = pbdev->dma_limit->avail;
} else {
dma_avail = 1;
}
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 24f6e60786..126a50c98b 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -279,7 +279,6 @@ struct S390PCIIOMMU {
uint64_t pba;
uint64_t pal;
uint64_t max_dma_limit;
- S390PCIDMACount *dma_limit;
};
typedef struct S390PCIIOMMUTable {
@@ -353,6 +352,7 @@ struct S390PCIBusDevice {
IOMMUMemoryRegion iommu_mr;
MemoryRegion *dm_mr;
GHashTable *iotlb;
+ S390PCIDMACount *dma_limit;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
IndAddr *indicator;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 09/16] s390x/pci: Move g_iota from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (7 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 08/16] s390x/pci: Move dma_limit " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 10/16] s390x/pci: Move pba " Konstantin Shkolnyy
` (6 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
This also allows to save/restore this field during migration.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 2 +-
hw/s390x/s390-pci-inst.c | 10 +++++-----
include/hw/s390x/s390-pci-bus.h | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index ad4f527430..ec54eb64a6 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -617,7 +617,7 @@ static void s390_pci_ioat_replay(S390PCIBusDevice *pbdev)
}
while (curr < end) {
- error = s390_guest_io_table_walk(iommu->g_iota, curr, &entry);
+ error = s390_guest_io_table_walk(pbdev->g_iota, curr, &entry);
if (error) {
pbdev->state = ZPCI_FS_ERROR;
s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, curr,
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index dc52da41b5..22b477ab38 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -765,7 +765,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
} else {
dma_avail = 1;
}
- if (!iommu->g_iota) {
+ if (!pbdev->g_iota) {
error = ERR_EVENT_INVALAS;
goto err;
}
@@ -785,7 +785,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
start = sstart;
again = false;
while (start < end) {
- error = s390_guest_io_table_walk(iommu->g_iota, start, &entry);
+ error = s390_guest_io_table_walk(pbdev->g_iota, start, &entry);
if (error) {
break;
}
@@ -1056,7 +1056,7 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
iommu->pba = pba;
iommu->pal = pal;
- iommu->g_iota = g_iota;
+ pbdev->g_iota = g_iota;
if (t) {
s390_pci_iommu_enable(pbdev);
@@ -1073,7 +1073,7 @@ void pci_dereg_ioat(S390PCIBusDevice *pbdev)
s390_pci_iommu_disable(pbdev);
iommu->pba = 0;
iommu->pal = 0;
- iommu->g_iota = 0;
+ pbdev->g_iota = 0;
}
void fmb_timer_free(S390PCIBusDevice *pbdev)
@@ -1446,7 +1446,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
stq_be_p(&fib.pba, pbdev->iommu->pba);
stq_be_p(&fib.pal, pbdev->iommu->pal);
- stq_be_p(&fib.iota, pbdev->iommu->g_iota);
+ stq_be_p(&fib.iota, pbdev->g_iota);
stq_be_p(&fib.aibv, pbdev->routes.adapter.ind_addr);
stq_be_p(&fib.aisb, pbdev->routes.adapter.summary_addr);
stq_be_p(&fib.fmb_addr, pbdev->fmb_addr);
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 126a50c98b..a7d1f1ade2 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -275,7 +275,6 @@ struct S390PCIIOMMU {
Object parent_obj;
AddressSpace as;
MemoryRegion mr;
- uint64_t g_iota;
uint64_t pba;
uint64_t pal;
uint64_t max_dma_limit;
@@ -352,6 +351,7 @@ struct S390PCIBusDevice {
IOMMUMemoryRegion iommu_mr;
MemoryRegion *dm_mr;
GHashTable *iotlb;
+ uint64_t g_iota;
S390PCIDMACount *dma_limit;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 10/16] s390x/pci: Move pba from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (8 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 09/16] s390x/pci: Move g_iota " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 11/16] s390x/pci: Move pal " Konstantin Shkolnyy
` (5 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
This also allows to save/restore this field during migration.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 4 ++--
hw/s390x/s390-pci-inst.c | 10 +++++-----
include/hw/s390x/s390-pci-bus.h | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index ec54eb64a6..6cf54b8ab6 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -565,7 +565,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
trace_s390_pci_iommu_xlate(addr);
- if (addr < iommu->pba || addr > iommu->pal) {
+ if (addr < pbdev->pba || addr > iommu->pal) {
error = ERR_EVENT_OORANGE;
goto err;
}
@@ -602,7 +602,7 @@ static void s390_pci_ioat_replay(S390PCIBusDevice *pbdev)
hwaddr curr, end;
S390PCIIOMMU *iommu = pbdev->iommu;
- curr = iommu->pba;
+ curr = pbdev->pba;
end = iommu->pal;
if (pbdev->dm_mr) {
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 22b477ab38..aa2bc98254 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -770,7 +770,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
goto err;
}
- if (end < start || end < iommu->pba || start > iommu->pal) {
+ if (end < start || end < pbdev->pba || start > iommu->pal) {
error = ERR_EVENT_OORANGE;
goto err;
}
@@ -778,7 +778,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
* If the specified range at least partially overlaps the registered
* aperture, clamp the request to the aperture and ignore the rest.
*/
- sstart = MAX(start, iommu->pba);
+ sstart = MAX(start, pbdev->pba);
end = MIN(end, iommu->pal + 1);
retry:
@@ -1054,7 +1054,7 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
return -EINVAL;
}
- iommu->pba = pba;
+ pbdev->pba = pba;
iommu->pal = pal;
pbdev->g_iota = g_iota;
@@ -1071,7 +1071,7 @@ void pci_dereg_ioat(S390PCIBusDevice *pbdev)
{
S390PCIIOMMU *iommu = pbdev->iommu;
s390_pci_iommu_disable(pbdev);
- iommu->pba = 0;
+ pbdev->pba = 0;
iommu->pal = 0;
pbdev->g_iota = 0;
}
@@ -1444,7 +1444,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
return 0;
}
- stq_be_p(&fib.pba, pbdev->iommu->pba);
+ stq_be_p(&fib.pba, pbdev->pba);
stq_be_p(&fib.pal, pbdev->iommu->pal);
stq_be_p(&fib.iota, pbdev->g_iota);
stq_be_p(&fib.aibv, pbdev->routes.adapter.ind_addr);
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index a7d1f1ade2..42f39a5cf0 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -275,7 +275,6 @@ struct S390PCIIOMMU {
Object parent_obj;
AddressSpace as;
MemoryRegion mr;
- uint64_t pba;
uint64_t pal;
uint64_t max_dma_limit;
};
@@ -352,6 +351,7 @@ struct S390PCIBusDevice {
MemoryRegion *dm_mr;
GHashTable *iotlb;
uint64_t g_iota;
+ uint64_t pba;
S390PCIDMACount *dma_limit;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 11/16] s390x/pci: Move pal from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (9 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 10/16] s390x/pci: Move pba " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 12/16] s390x/pci: Move max_dma_limit " Konstantin Shkolnyy
` (4 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
This also allows to save/restore this field during migration.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 8 +++-----
hw/s390x/s390-pci-inst.c | 14 +++++---------
include/hw/s390x/s390-pci-bus.h | 2 +-
3 files changed, 9 insertions(+), 15 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 6cf54b8ab6..79bef96c4d 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -540,7 +540,6 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
IOMMUAccessFlags flag, int iommu_idx)
{
S390PCIBusDevice *pbdev = container_of(mr, S390PCIBusDevice, iommu_mr);
- S390PCIIOMMU *iommu = pbdev->iommu;
S390IOTLBEntry *entry;
uint64_t iova = addr & TARGET_PAGE_MASK;
uint16_t error = 0;
@@ -565,7 +564,7 @@ static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr,
trace_s390_pci_iommu_xlate(addr);
- if (addr < pbdev->pba || addr > iommu->pal) {
+ if (addr < pbdev->pba || addr > pbdev->pal) {
error = ERR_EVENT_OORANGE;
goto err;
}
@@ -600,10 +599,9 @@ static void s390_pci_ioat_replay(S390PCIBusDevice *pbdev)
uint16_t error = 0;
uint32_t dma_avail;
hwaddr curr, end;
- S390PCIIOMMU *iommu = pbdev->iommu;
curr = pbdev->pba;
- end = iommu->pal;
+ end = pbdev->pal;
if (pbdev->dm_mr) {
/* If direct mapping is used, there are no guest tables to replay */
@@ -788,7 +786,7 @@ void s390_pci_iommu_enable(S390PCIBusDevice *pbdev)
char *name = g_strdup_printf("iommu-s390-%04x", pbdev->uid);
memory_region_init_iommu(&pbdev->iommu_mr, sizeof(pbdev->iommu_mr),
TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr),
- name, iommu->pal + 1);
+ name, pbdev->pal + 1);
pbdev->iommu_enabled = true;
memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&pbdev->iommu_mr));
g_free(name);
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index aa2bc98254..f93db10c81 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -717,7 +717,6 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
uint32_t fh;
uint16_t error = 0;
S390PCIBusDevice *pbdev;
- S390PCIIOMMU *iommu;
S390IOTLBEntry entry;
hwaddr start, end, sstart;
uint32_t dma_avail;
@@ -759,7 +758,6 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
break;
}
- iommu = pbdev->iommu;
if (pbdev->dma_limit) {
dma_avail = pbdev->dma_limit->avail;
} else {
@@ -770,7 +768,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
goto err;
}
- if (end < start || end < pbdev->pba || start > iommu->pal) {
+ if (end < start || end < pbdev->pba || start > pbdev->pal) {
error = ERR_EVENT_OORANGE;
goto err;
}
@@ -779,7 +777,7 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra)
* aperture, clamp the request to the aperture and ignore the rest.
*/
sstart = MAX(start, pbdev->pba);
- end = MIN(end, iommu->pal + 1);
+ end = MIN(end, pbdev->pal + 1);
retry:
start = sstart;
@@ -1029,7 +1027,6 @@ bool s390_pci_is_translation_enabled(uint64_t g_iota)
static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
uintptr_t ra)
{
- S390PCIIOMMU *iommu = pbdev->iommu;
uint64_t pba = ldq_be_p(&fib.pba);
uint64_t pal = ldq_be_p(&fib.pal);
uint64_t g_iota = ldq_be_p(&fib.iota);
@@ -1055,7 +1052,7 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
}
pbdev->pba = pba;
- iommu->pal = pal;
+ pbdev->pal = pal;
pbdev->g_iota = g_iota;
if (t) {
@@ -1069,10 +1066,9 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
void pci_dereg_ioat(S390PCIBusDevice *pbdev)
{
- S390PCIIOMMU *iommu = pbdev->iommu;
s390_pci_iommu_disable(pbdev);
pbdev->pba = 0;
- iommu->pal = 0;
+ pbdev->pal = 0;
pbdev->g_iota = 0;
}
@@ -1445,7 +1441,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
}
stq_be_p(&fib.pba, pbdev->pba);
- stq_be_p(&fib.pal, pbdev->iommu->pal);
+ stq_be_p(&fib.pal, pbdev->pal);
stq_be_p(&fib.iota, pbdev->g_iota);
stq_be_p(&fib.aibv, pbdev->routes.adapter.ind_addr);
stq_be_p(&fib.aisb, pbdev->routes.adapter.summary_addr);
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 42f39a5cf0..1f5d8d4bd4 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -275,7 +275,6 @@ struct S390PCIIOMMU {
Object parent_obj;
AddressSpace as;
MemoryRegion mr;
- uint64_t pal;
uint64_t max_dma_limit;
};
@@ -352,6 +351,7 @@ struct S390PCIBusDevice {
GHashTable *iotlb;
uint64_t g_iota;
uint64_t pba;
+ uint64_t pal;
S390PCIDMACount *dma_limit;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 12/16] s390x/pci: Move max_dma_limit from S390PCIIOMMU to S390PCIBusDevice
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (10 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 11/16] s390x/pci: Move pal " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 13/16] s390x/pci: Add a comment explaining S390PCIIOMMU purpose Konstantin Shkolnyy
` (3 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
This field is only used when S390PCIBusDevice exists, so it can be moved
there to simplify S390PCIIOMMU towards a structure that contains only the
IOMMU container information needed by the PCI layer for a given slot.
Reviewed-by: Farhan Ali <alifm@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-vfio.c | 4 ++--
include/hw/s390x/s390-pci-bus.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c
index db6de00bd2..a035ba5b2c 100644
--- a/hw/s390x/s390-pci-vfio.c
+++ b/hw/s390x/s390-pci-vfio.c
@@ -90,7 +90,7 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s,
cnt->users = 1;
cnt->avail = avail;
QTAILQ_INSERT_TAIL(&s->zpci_dma_limit, cnt, link);
- pbdev->iommu->max_dma_limit = avail;
+ pbdev->max_dma_limit = avail;
return cnt;
}
@@ -152,7 +152,7 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev,
* to request that the guest free DMA mappings as necessary.
*/
if (!pbdev->rtr_avail) {
- vfio_size = pbdev->iommu->max_dma_limit << qemu_target_page_bits();
+ vfio_size = pbdev->max_dma_limit << qemu_target_page_bits();
if (vfio_size > 0 && vfio_size < cap->end_dma - cap->start_dma + 1) {
pbdev->zpci_fn.edma = cap->start_dma + vfio_size - 1;
}
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 1f5d8d4bd4..e3cbee2695 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -275,7 +275,6 @@ struct S390PCIIOMMU {
Object parent_obj;
AddressSpace as;
MemoryRegion mr;
- uint64_t max_dma_limit;
};
typedef struct S390PCIIOMMUTable {
@@ -352,6 +351,7 @@ struct S390PCIBusDevice {
uint64_t g_iota;
uint64_t pba;
uint64_t pal;
+ uint64_t max_dma_limit;
S390PCIDMACount *dma_limit;
MemoryRegion msix_notify_mr;
IndAddr *summary_ind;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 13/16] s390x/pci: Add a comment explaining S390PCIIOMMU purpose
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (11 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 12/16] s390x/pci: Move max_dma_limit " Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 14/16] s390x/pci: Factor ioat sanity checks into a separate function Konstantin Shkolnyy
` (2 subsequent siblings)
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
Now that S390PCIIOMMU had been reduced to only the IOMMU container
information needed by the PCI layer for the associated slot, add a
comment explaining the purpose of the structure.
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
include/hw/s390x/s390-pci-bus.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index e3cbee2695..0a59be1fd0 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -271,6 +271,14 @@ typedef struct S390PCIDMACount {
QTAILQ_ENTRY(S390PCIDMACount) link;
} S390PCIDMACount;
+/*
+ * This structure holds the AddressSpace for a PCI device slot. It must be
+ * allocated before PCIDevice registration completes, specifically to satisfy
+ * the get_address_space IOMMU callback invoked by do_pci_register_device().
+ * The root MemoryRegion is otherwise empty; DMA translations only become
+ * functional once the guest configures the device, at which point
+ * s390_pci_iommu_enable() registers the zPCI translation subregions beneath it.
+ */
struct S390PCIIOMMU {
Object parent_obj;
AddressSpace as;
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v8 14/16] s390x/pci: Factor ioat sanity checks into a separate function
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (12 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 13/16] s390x/pci: Add a comment explaining S390PCIIOMMU purpose Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-16 18:53 ` Farhan Ali
2026-09-11 15:21 ` [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
2026-09-11 15:21 ` [PATCH v8 16/16] s390x/pci: Create function to contain fmb_timer start Konstantin Shkolnyy
15 siblings, 1 reply; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
Extract the pba, pal and g_iota checks from reg_ioat() into a function.
Make it external so that it can also be used in a follow up change.
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-inst.c | 39 +++++++++++++++++++++-----------
include/hw/s390x/s390-pci-inst.h | 2 ++
2 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index f93db10c81..c7f96417cc 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -1024,29 +1024,42 @@ bool s390_pci_is_translation_enabled(uint64_t g_iota)
return ((g_iota >> 11) & 0x1) != 0; /* "T" bit */
}
+bool s390_pci_ioat_validate(S390PCIBusDevice *pbdev, uint64_t pba,
+ uint64_t pal, uint64_t g_iota, bool report)
+{
+ uint8_t dt = (g_iota >> 2) & 0x7;
+ bool t = s390_pci_is_translation_enabled(g_iota);
+
+ if (pba > pal || pba < pbdev->zpci_fn.sdma || pal > pbdev->zpci_fn.edma) {
+ return false;
+ }
+ /* currently we only support designation type 1 with translation */
+ if (t && dt != ZPCI_IOTA_RTTO) {
+ if (report) {
+ error_report("unsupported ioat dt %d t %d", dt, t);
+ }
+ return false;
+ }
+ if (!t && !pbdev->rtr_avail) {
+ if (report) {
+ error_report("relaxed translation not allowed");
+ }
+ return false;
+ }
+ return true;
+}
+
static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
uintptr_t ra)
{
uint64_t pba = ldq_be_p(&fib.pba);
uint64_t pal = ldq_be_p(&fib.pal);
uint64_t g_iota = ldq_be_p(&fib.iota);
- uint8_t dt = (g_iota >> 2) & 0x7;
bool t = s390_pci_is_translation_enabled(g_iota);
pba &= ~0xfff;
pal |= 0xfff;
- if (pba > pal || pba < pbdev->zpci_fn.sdma || pal > pbdev->zpci_fn.edma) {
- s390_program_interrupt(env, PGM_OPERAND, ra);
- return -EINVAL;
- }
-
- /* currently we only support designation type 1 with translation */
- if (t && dt != ZPCI_IOTA_RTTO) {
- error_report("unsupported ioat dt %d t %d", dt, t);
- s390_program_interrupt(env, PGM_OPERAND, ra);
- return -EINVAL;
- } else if (!t && !pbdev->rtr_avail) {
- error_report("relaxed translation not allowed");
+ if (!s390_pci_ioat_validate(pbdev, pba, pal, g_iota, /*report=*/true)) {
s390_program_interrupt(env, PGM_OPERAND, ra);
return -EINVAL;
}
diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
index 38268c256e..b53113ddbc 100644
--- a/include/hw/s390x/s390-pci-inst.h
+++ b/include/hw/s390x/s390-pci-inst.h
@@ -100,6 +100,8 @@ typedef struct ZpciFib {
int pci_dereg_irqs(S390PCIBusDevice *pbdev);
void pci_dereg_ioat(S390PCIBusDevice *pbdev);
+bool s390_pci_ioat_validate(S390PCIBusDevice *pbdev, uint64_t pba,
+ uint64_t pal, uint64_t g_iota, bool report);
int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra);
int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra);
int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra);
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v8 14/16] s390x/pci: Factor ioat sanity checks into a separate function
2026-09-11 15:21 ` [PATCH v8 14/16] s390x/pci: Factor ioat sanity checks into a separate function Konstantin Shkolnyy
@ 2026-09-16 18:53 ` Farhan Ali
0 siblings, 0 replies; 26+ messages in thread
From: Farhan Ali @ 2026-09-16 18:53 UTC (permalink / raw)
To: Konstantin Shkolnyy, mjrosato
Cc: farman, richard.henderson, iii, david, cohuck, pasic, borntraeger,
qemu-s390x, qemu-devel
On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
> Extract the pba, pal and g_iota checks from reg_ioat() into a function.
> Make it external so that it can also be used in a follow up change.
>
> Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
> ---
> hw/s390x/s390-pci-inst.c | 39 +++++++++++++++++++++-----------
> include/hw/s390x/s390-pci-inst.h | 2 ++
> 2 files changed, 28 insertions(+), 13 deletions(-)
>
> diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
> index f93db10c81..c7f96417cc 100644
> --- a/hw/s390x/s390-pci-inst.c
> +++ b/hw/s390x/s390-pci-inst.c
> @@ -1024,29 +1024,42 @@ bool s390_pci_is_translation_enabled(uint64_t g_iota)
> return ((g_iota >> 11) & 0x1) != 0; /* "T" bit */
> }
>
> +bool s390_pci_ioat_validate(S390PCIBusDevice *pbdev, uint64_t pba,
> + uint64_t pal, uint64_t g_iota, bool report)
> +{
Would it be better to pass Error **errp instead of report? I think QEMU
that is the preferred QEMU style.
> + uint8_t dt = (g_iota >> 2) & 0x7;
> + bool t = s390_pci_is_translation_enabled(g_iota);
> +
> + if (pba > pal || pba < pbdev->zpci_fn.sdma || pal > pbdev->zpci_fn.edma) {
> + return false;
> + }
> + /* currently we only support designation type 1 with translation */
> + if (t && dt != ZPCI_IOTA_RTTO) {
> + if (report) {
> + error_report("unsupported ioat dt %d t %d", dt, t);
> + }
> + return false;
> + }
> + if (!t && !pbdev->rtr_avail) {
> + if (report) {
> + error_report("relaxed translation not allowed");
> + }
> + return false;
> + }
> + return true;
> +}
> +
> static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib,
> uintptr_t ra)
> {
> uint64_t pba = ldq_be_p(&fib.pba);
> uint64_t pal = ldq_be_p(&fib.pal);
> uint64_t g_iota = ldq_be_p(&fib.iota);
> - uint8_t dt = (g_iota >> 2) & 0x7;
> bool t = s390_pci_is_translation_enabled(g_iota);
>
> pba &= ~0xfff;
> pal |= 0xfff;
> - if (pba > pal || pba < pbdev->zpci_fn.sdma || pal > pbdev->zpci_fn.edma) {
> - s390_program_interrupt(env, PGM_OPERAND, ra);
> - return -EINVAL;
> - }
> -
> - /* currently we only support designation type 1 with translation */
> - if (t && dt != ZPCI_IOTA_RTTO) {
> - error_report("unsupported ioat dt %d t %d", dt, t);
> - s390_program_interrupt(env, PGM_OPERAND, ra);
> - return -EINVAL;
> - } else if (!t && !pbdev->rtr_avail) {
> - error_report("relaxed translation not allowed");
> + if (!s390_pci_ioat_validate(pbdev, pba, pal, g_iota, /*report=*/true)) {
> s390_program_interrupt(env, PGM_OPERAND, ra);
> return -EINVAL;
> }
> diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
> index 38268c256e..b53113ddbc 100644
> --- a/include/hw/s390x/s390-pci-inst.h
> +++ b/include/hw/s390x/s390-pci-inst.h
> @@ -100,6 +100,8 @@ typedef struct ZpciFib {
>
> int pci_dereg_irqs(S390PCIBusDevice *pbdev);
> void pci_dereg_ioat(S390PCIBusDevice *pbdev);
> +bool s390_pci_ioat_validate(S390PCIBusDevice *pbdev, uint64_t pba,
> + uint64_t pal, uint64_t g_iota, bool report);
> int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra);
> int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra);
> int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra);
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (13 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 14/16] s390x/pci: Factor ioat sanity checks into a separate function Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
2026-09-17 17:25 ` Farhan Ali
2026-09-11 15:21 ` [PATCH v8 16/16] s390x/pci: Create function to contain fmb_timer start Konstantin Shkolnyy
15 siblings, 1 reply; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
Implement zPCI device state migration, consequently enabling migration
of VMs that have emulated PCI devices, whether virtio or not.
Migration is allowed for devices whose function handle has the
FH_SHM_EMUL bit set. For these devices QEMU will save and restore the
state of its zPCI emulator.
This will enable emulated PCI migration starting with s390-ccw-virtio-11.2.
Passthrough devices will continue to block migration.
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 306 ++++++++++++++++++++++++++++++-
hw/s390x/s390-pci-inst.c | 2 +-
hw/s390x/s390-virtio-ccw.c | 4 +
include/hw/s390x/s390-pci-bus.h | 13 ++
include/hw/s390x/s390-pci-inst.h | 1 +
5 files changed, 317 insertions(+), 9 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index 79bef96c4d..c16941ace6 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -26,6 +26,8 @@
#include "hw/pci/pci_bridge.h"
#include "hw/pci/msi.h"
#include "exec/cpu-common.h"
+#include "migration/blocker.h"
+#include "migration/vmstate.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "system/physmem.h"
@@ -922,6 +924,8 @@ static void set_pbdev_info(S390PCIBusDevice *pbdev)
pbdev->pci_group = s390_group_find(ZPCI_DEFAULT_FN_GRP);
}
+static const VMStateDescription vmstate_s390_pcihost;
+
static void s390_pcihost_realize(DeviceState *dev, Error **errp)
{
PCIBus *b;
@@ -956,6 +960,7 @@ static void s390_pcihost_realize(DeviceState *dev, Error **errp)
s390_pci_init_default_group();
css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false,
S390_ADAPTER_SUPPRESSIBLE, errp);
+ vmstate_register(VMSTATE_IF(dev), 0, &vmstate_s390_pcihost, s);
s390_pcihost_kvm_realize();
}
@@ -1134,12 +1139,47 @@ static int s390_pci_interp_plug(S390pciState *s, S390PCIBusDevice *pbdev)
return 0;
}
+static int s390_set_zpci_migration_blocker(S390PCIBusDevice *pbdev,
+ S390pciState *s, Error **errp)
+{
+ if (s->zpci_migr_enabled) {
+ return 0;
+ }
+ error_setg(&pbdev->zpci_migr_blocker,
+ "Migration blocked on this machine type by zPCI device "
+ "uid %d", pbdev->uid);
+ return migrate_add_blocker(&pbdev->zpci_migr_blocker, errp);
+}
+
+static void s390_clear_zpci_migration_blocker(S390PCIBusDevice *pbdev)
+{
+ migrate_del_blocker(&pbdev->zpci_migr_blocker);
+}
+
+static int s390_set_passthrough_migration_blocker(S390PCIBusDevice *pbdev,
+ Error **errp)
+{
+ if (pbdev->fh & FH_SHM_EMUL) {
+ return 0;
+ }
+ error_setg(&pbdev->passthrough_migr_blocker,
+ "Migration blocked by passthrough zPCI device uid %d",
+ pbdev->uid);
+ return migrate_add_blocker(&pbdev->passthrough_migr_blocker, errp);
+}
+
+static void s390_clear_passthrough_migration_blocker(S390PCIBusDevice *pbdev)
+{
+ migrate_del_blocker(&pbdev->passthrough_migr_blocker);
+}
+
static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
PCIDevice *pdev = NULL;
S390PCIBusDevice *pbdev = NULL;
+ bool auto_pbdev = false;
int rc;
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
@@ -1199,6 +1239,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
if (!pbdev) {
return;
}
+ auto_pbdev = true;
}
pbdev->pdev = pdev;
@@ -1218,7 +1259,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
if (rc) {
error_setg(errp, "Plug failed for zPCI device in "
"interpretation mode: %d", rc);
- return;
+ goto err_unlink_pbdev;
}
} else {
trace_s390_pcihost("zPCI interpretation missing");
@@ -1247,16 +1288,44 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
pbdev->rtr_avail = false;
}
+ if (s390_set_passthrough_migration_blocker(pbdev, errp) != 0) {
+ goto err_unlink_pbdev;
+ }
+
if (s390_pci_msix_init(pbdev) && !pbdev->interp) {
error_setg(errp, "MSI-X support is mandatory "
"in the S390 architecture");
- return;
+ s390_clear_passthrough_migration_blocker(pbdev);
+ goto err_unlink_pbdev;
}
if (dev->hotplugged) {
s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED ,
pbdev->fh, pbdev->fid);
}
+ return;
+
+err_unlink_pbdev:
+ if (pbdev->pft == ZPCI_PFT_ISM) {
+ notifier_remove(&pbdev->shutdown_notifier);
+ }
+ if (pbdev->dma_limit) {
+ s390_pci_end_dma_count(s, pbdev->dma_limit);
+ pbdev->dma_limit = NULL;
+ }
+ pbdev->fh &= ~FH_MASK_SHM;
+ pbdev->iommu = NULL;
+ pbdev->pdev = NULL;
+ if (auto_pbdev) {
+ QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
+ if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
+ g_hash_table_remove(s->zpci_table, &pbdev->idx);
+ }
+ g_hash_table_destroy(pbdev->iotlb);
+ s390_clear_zpci_migration_blocker(pbdev);
+ qdev_unrealize(DEVICE(pbdev));
+ }
+ return;
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
pbdev = S390_PCI_DEVICE(dev);
@@ -1267,6 +1336,12 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
NULL, g_free);
QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link);
g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev);
+ if (s390_set_zpci_migration_blocker(pbdev, s, errp) != 0) {
+ g_hash_table_remove(s->zpci_table, &pbdev->idx);
+ QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
+ g_hash_table_destroy(pbdev->iotlb);
+ return;
+ }
} else {
g_assert_not_reached();
}
@@ -1289,6 +1364,8 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
+ s390_clear_passthrough_migration_blocker(pbdev);
+
s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
pbdev->fh, pbdev->fid);
bus = pci_get_bus(pci_dev);
@@ -1303,11 +1380,22 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
pbdev = S390_PCI_DEVICE(dev);
pbdev->fid = 0;
QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
- g_hash_table_remove(s->zpci_table, &pbdev->idx);
+ /*
+ * If this QEMU is running a migrated guest, and was configured with
+ * more zpci devices than the source QEMU, extra zpci devices could be
+ * excluded from zpci_table and invisible to the guest and have
+ * pbdev->idx values duplicating those of active devices. If the
+ * pbdev being unplugged is such, the zpci_table entry for pbdev->idx
+ * can find a different pbdev which we must not remove.
+ */
+ if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
+ g_hash_table_remove(s->zpci_table, &pbdev->idx);
+ }
if (pbdev->dma_limit) {
s390_pci_end_dma_count(s, pbdev->dma_limit);
}
g_hash_table_destroy(pbdev->iotlb);
+ s390_clear_zpci_migration_blocker(pbdev);
qdev_unrealize(dev);
}
}
@@ -1443,6 +1531,65 @@ static void s390_pcihost_reset(DeviceState *dev)
pci_for_each_device_under_bus(bus, s390_pci_enumerate_bridge, s);
}
+/*
+ * TYPE_S390_PCI_HOST_BRIDGE device state migration is registered via
+ * vmstate_register() rather than dc->vmsd because dc->vmsd is already assigned
+ * by our base, TYPE_PCI_HOST_BRIDGE, to &vmstate_pcihost which migrates
+ * PCIHostState.config_reg. There is no mechanism to add a subclass vmsd to the
+ * parent's.
+ */
+static bool vmstate_s390_pcihost_pending_sei_needed(void *opaque)
+{
+ S390pciState *s = S390_PCI_HOST_BRIDGE(opaque);
+ return s->zpci_migr_enabled && !QTAILQ_EMPTY(&s->pending_sei);
+}
+
+/* Per-element descriptor for pending_sei list */
+static const VMStateDescription vmstate_sei_container = {
+ .name = "s390_sei_container",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(fid, SeiContainer),
+ VMSTATE_UINT32(fh, SeiContainer),
+ VMSTATE_UINT8(cc, SeiContainer),
+ VMSTATE_UINT16(pec, SeiContainer),
+ VMSTATE_UINT64(faddr, SeiContainer),
+ VMSTATE_UINT32(e, SeiContainer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_s390_pcihost_pending_sei = {
+ .name = TYPE_S390_PCI_HOST_BRIDGE "/pending-sei",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = vmstate_s390_pcihost_pending_sei_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_QTAILQ_V(pending_sei, S390pciState, 1,
+ vmstate_sei_container, SeiContainer, link),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_s390_pcihost = {
+ .name = TYPE_S390_PCI_HOST_BRIDGE,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_s390_pcihost_pending_sei,
+ NULL
+ }
+};
+
+static const Property phb_props[] = {
+ DEFINE_PROP_BOOL("x-zpci-migr-enabled", S390pciState,
+ zpci_migr_enabled, true),
+};
+
static void s390_pcihost_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1456,6 +1603,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, const void *data)
hc->unplug_request = s390_pcihost_unplug_request;
hc->unplug = s390_pcihost_unplug;
msi_nonbroken = true;
+ device_class_set_props(dc, phb_props);
}
static const TypeInfo s390_pcihost_info = {
@@ -1469,10 +1617,33 @@ static const TypeInfo s390_pcihost_info = {
}
};
+/* Return a unique bus "path" for zpci device */
+static char *s390_pci_bus_get_dev_path(DeviceState *dev)
+{
+ S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev);
+ return g_strdup_printf("uid-%04x", pbdev->uid);
+}
+
+static void s390_pcibus_class_init(ObjectClass *oc, const void *data)
+{
+ BusClass *bc = BUS_CLASS(oc);
+ bc->get_dev_path = s390_pci_bus_get_dev_path;
+}
+
static const TypeInfo s390_pcibus_info = {
.name = TYPE_S390_PCI_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(S390PCIBus),
+ /*
+ * Implement get_dev_path() to provide each zpci device with a unique
+ * stable UID-based bus "path". The "path" is used as part of idstr in the
+ * migration stream, making idstr unique and instance_id always 0.
+ * For migration to succeed, (idstr+instance_id) must match those generated
+ * during QEMU start. Without unique idstr, QEMU will generate variable
+ * instance_id to distinguish devices, and that instance_id can change
+ * if a device is unplugged and plugged back, preventing migration.
+ */
+ .class_init = s390_pcibus_class_init,
};
static uint16_t s390_pci_generate_uid(S390pciState *s)
@@ -1618,13 +1789,132 @@ static const Property s390_pci_device_properties[] = {
true),
};
-static const VMStateDescription s390_pci_device_vmstate = {
- .name = TYPE_S390_PCI_DEVICE,
+static int s390_pci_device_pre_load(void *opaque)
+{
+ S390PCIBusDevice *pbdev = S390_PCI_DEVICE(opaque);
+ S390PCIBusDevice *found_pbdev;
+
/*
- * TODO: add state handling here, so migration works at least with
- * emulated pci devices on s390x
+ * Because state loading can change pbdev->idx make sure pbdev is removed
+ * from the table before that happens. The table type used stores a pointer
+ * to pbdev->idx and becomes corrupt if idx is changed from outside. But be
+ * careful to not remove instead another pbdev whose state might have been
+ * loaded earlier and that got assigned this idx value and had therefore
+ * already replaced our pbdev in the table. post_load() will reinsert our
+ * pbdev into the table. (found_pbdev could even be 0 if a previous
+ * vmstate_load_vmsd() on this device failed before reaching post_load().)
*/
- .unmigratable = 1,
+ found_pbdev = g_hash_table_lookup(s390_get_phb()->zpci_table, &pbdev->idx);
+ if (found_pbdev == pbdev) {
+ g_hash_table_remove(s390_get_phb()->zpci_table, &pbdev->idx);
+ }
+
+ return 0;
+}
+
+static bool s390_pci_device_post_load_errp(void *opaque, int version_id,
+ Error **errp)
+{
+ S390PCIBusDevice *pbdev = S390_PCI_DEVICE(opaque);
+
+ pbdev->zpci_fn.fid = pbdev->fid;
+ pbdev->zpci_fn.uid = pbdev->uid;
+
+ /*
+ * Now that pbdev->idx has been loaded, use it to place pbdev back into
+ * the table. This may replace a different not-yet-state-loaded pbdev,
+ * but pre_load() handles this case.
+ */
+ g_hash_table_replace(s390_get_phb()->zpci_table, &pbdev->idx, pbdev);
+
+ /*
+ * Regenerate IOMMU state, including IOTLB contents and QEMU memory regions.
+ */
+ if (pbdev->iommu_enabled) {
+ if (!pbdev->iommu) {
+ error_setg(errp, "iommu is NULL");
+ return false;
+ }
+ if (!s390_pci_ioat_validate(pbdev, pbdev->pba, pbdev->pal,
+ pbdev->g_iota, /*report=*/false)) {
+ error_setg(errp, "invalid pba, pal or g_iota in migration stream");
+ return false;
+ }
+ if (s390_pci_is_translation_enabled(pbdev->g_iota)) {
+ s390_pci_iommu_enable(pbdev);
+ s390_pci_ioat_replay(pbdev);
+ } else {
+ /* TODO: unreachable until vfio passthrough migration is enabled */
+ s390_pci_iommu_direct_map_enable(pbdev);
+ }
+ }
+
+ /*
+ * Guest sets fmb_addr by mpcifc.ZPCI_MOD_FC_SET_MEASURE instruction,
+ * whose handler consequently starts fmb_timer. We may need to restart it.
+ */
+ if (pbdev->fmb_addr) {
+ if (pbdev->fmb_timer) {
+ error_setg(errp, "fmb_timer is not NULL");
+ return false;
+ }
+ if (!pbdev->pci_group) {
+ error_setg(errp, "pci_group is NULL");
+ return false;
+ }
+ pbdev->fmb_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ fmb_update, pbdev);
+ timer_mod(pbdev->fmb_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
+ pbdev->pci_group->zpci_group.mui);
+ }
+ return true;
+}
+
+static const VMStateDescription s390_pci_device_vmstate = {
+ .name = TYPE_S390_PCI_DEVICE,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .priority = MIG_PRI_IOMMU,
+ .pre_load = s390_pci_device_pre_load,
+ .post_load_errp = s390_pci_device_post_load_errp,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(state, S390PCIBusDevice),
+ VMSTATE_UINT16(uid, S390PCIBusDevice),
+ VMSTATE_UINT32(idx, S390PCIBusDevice),
+ VMSTATE_UINT32(fh, S390PCIBusDevice),
+ VMSTATE_UINT32(fid, S390PCIBusDevice),
+ VMSTATE_BOOL(fid_defined, S390PCIBusDevice),
+ VMSTATE_UINT64(fmb_addr, S390PCIBusDevice),
+ VMSTATE_UINT32(fmb.format, S390PCIBusDevice),
+ VMSTATE_UINT32(fmb.sample, S390PCIBusDevice),
+ VMSTATE_UINT64(fmb.last_update, S390PCIBusDevice),
+ VMSTATE_UINT64_ARRAY(fmb.counter, S390PCIBusDevice,
+ ARRAY_SIZE(((S390PCIBusDevice *)0)->fmb.counter)),
+ VMSTATE_UINT64(fmb.fmt0.dma_rbytes, S390PCIBusDevice),
+ VMSTATE_UINT64(fmb.fmt0.dma_wbytes, S390PCIBusDevice),
+ VMSTATE_UINT8(isc, S390PCIBusDevice),
+ VMSTATE_UINT16(noi, S390PCIBusDevice),
+ VMSTATE_UINT8(sum, S390PCIBusDevice),
+ VMSTATE_UINT8(pft, S390PCIBusDevice),
+ VMSTATE_UINT64(routes.adapter.ind_addr, S390PCIBusDevice),
+ VMSTATE_UINT64(routes.adapter.summary_addr, S390PCIBusDevice),
+ VMSTATE_UINT64(routes.adapter.ind_offset, S390PCIBusDevice),
+ VMSTATE_UINT32(routes.adapter.summary_offset, S390PCIBusDevice),
+ VMSTATE_UINT32(routes.adapter.adapter_id, S390PCIBusDevice),
+ VMSTATE_BOOL(iommu_enabled, S390PCIBusDevice),
+ VMSTATE_UINT64(g_iota, S390PCIBusDevice),
+ VMSTATE_UINT64(pba, S390PCIBusDevice),
+ VMSTATE_UINT64(pal, S390PCIBusDevice),
+ VMSTATE_PTR_TO_IND_ADDR(summary_ind, S390PCIBusDevice),
+ VMSTATE_PTR_TO_IND_ADDR(indicator, S390PCIBusDevice),
+ VMSTATE_BOOL(unplug_requested, S390PCIBusDevice),
+ VMSTATE_BOOL(interp, S390PCIBusDevice),
+ VMSTATE_BOOL(forwarding_assist, S390PCIBusDevice),
+ VMSTATE_BOOL(aif, S390PCIBusDevice),
+ VMSTATE_BOOL(rtr_avail, S390PCIBusDevice),
+ VMSTATE_END_OF_LIST()
+ }
};
static void s390_pci_device_class_init(ObjectClass *klass, const void *data)
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index c7f96417cc..9b701b4dff 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -1135,7 +1135,7 @@ static int fmb_do_update(S390PCIBusDevice *pbdev, int offset, uint64_t val,
return ret;
}
-static void fmb_update(void *opaque)
+void fmb_update(void *opaque)
{
S390PCIBusDevice *pbdev = opaque;
int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 17266779a6..db0b49375e 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -1005,12 +1005,16 @@ static void ccw_machine_11_1_instance_options(MachineState *machine)
static void ccw_machine_11_1_class_options(MachineClass *mc)
{
S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc);
+ static GlobalProperty compat[] = {
+ { TYPE_S390_PCI_HOST_BRIDGE, "x-zpci-migr-enabled", "off" },
+ };
s390mc->use_certs = false;
s390mc->use_secure = false;
ccw_machine_11_2_class_options(mc);
compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len);
+ compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat));
}
DEFINE_CCW_MACHINE(11, 1);
diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h
index 0a59be1fd0..d310a46e8f 100644
--- a/include/hw/s390x/s390-pci-bus.h
+++ b/include/hw/s390x/s390-pci-bus.h
@@ -337,6 +337,8 @@ struct S390PCIBusDevice {
uint16_t uid;
uint32_t idx;
uint32_t fh;
+ Error *zpci_migr_blocker; /* machines 11.1 or older */
+ Error *passthrough_migr_blocker;
uint32_t fid;
bool fid_defined;
uint64_t fmb_addr;
@@ -385,11 +387,22 @@ struct S390pciState {
S390PCIBus *bus;
GHashTable *iommu_table;
GHashTable *zpci_table;
+ /*
+ * pending_sei is the zPCI payload queue drained by CHSC SEI (one
+ * SeiContainer per instruction execution). It is the zPCI-layer
+ * counterpart of ChannelSubSys.pending_crws, which carries the
+ * architectural CRW notifications that prompt the guest to issue
+ * CHSC SEI in the first place. pending_crws is migrated by
+ * vmstate_css; pending_sei is migrated by vmstate_s390_pcihost_pending_sei
+ * in s390-pci-bus.c so that the two queues remain consistent on the
+ * destination.
+ */
QTAILQ_HEAD(, SeiContainer) pending_sei;
QTAILQ_HEAD(, S390PCIBusDevice) zpci_devs;
QTAILQ_HEAD(, S390PCIDMACount) zpci_dma_limit;
QTAILQ_HEAD(, S390PCIGroup) zpci_groups;
uint8_t next_sim_grp;
+ bool zpci_migr_enabled;
};
S390pciState *s390_get_phb(void);
diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
index b53113ddbc..873773f621 100644
--- a/include/hw/s390x/s390-pci-inst.h
+++ b/include/hw/s390x/s390-pci-inst.h
@@ -113,6 +113,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra);
void fmb_timer_free(S390PCIBusDevice *pbdev);
+void fmb_update(void *opaque);
uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev, S390IOTLBEntry *entry);
#define ZPCI_IO_BAR_MIN 0
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices
2026-09-11 15:21 ` [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
@ 2026-09-17 17:25 ` Farhan Ali
2026-09-17 17:35 ` Daniel P. Berrangé
0 siblings, 1 reply; 26+ messages in thread
From: Farhan Ali @ 2026-09-17 17:25 UTC (permalink / raw)
To: Konstantin Shkolnyy, mjrosato
Cc: farman, richard.henderson, iii, david, cohuck, pasic, borntraeger,
qemu-s390x, qemu-devel
On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
> @@ -1303,11 +1380,22 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
> pbdev = S390_PCI_DEVICE(dev);
> pbdev->fid = 0;
> QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
> - g_hash_table_remove(s->zpci_table, &pbdev->idx);
> + /*
> + * If this QEMU is running a migrated guest, and was configured with
> + * more zpci devices than the source QEMU, extra zpci devices could be
> + * excluded from zpci_table and invisible to the guest and have
> + * pbdev->idx values duplicating those of active devices. If the
> + * pbdev being unplugged is such, the zpci_table entry for pbdev->idx
> + * can find a different pbdev which we must not remove.
> + */
> + if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
> + g_hash_table_remove(s->zpci_table, &pbdev->idx);
> + }
If we are migrating, won't we re-create the exact same state and devices
on the target host? Is it possible that the target QEMU can have more
devices than source?
> if (pbdev->dma_limit) {
> s390_pci_end_dma_count(s, pbdev->dma_limit);
> }
> g_hash_table_destroy(pbdev->iotlb);
> + s390_clear_zpci_migration_blocker(pbdev);
> qdev_unrealize(dev);
> }
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices
2026-09-17 17:25 ` Farhan Ali
@ 2026-09-17 17:35 ` Daniel P. Berrangé
2026-09-21 15:12 ` Konstantin Shkolnyy
0 siblings, 1 reply; 26+ messages in thread
From: Daniel P. Berrangé @ 2026-09-17 17:35 UTC (permalink / raw)
To: Farhan Ali
Cc: Konstantin Shkolnyy, mjrosato, farman, richard.henderson, iii,
david, cohuck, pasic, borntraeger, qemu-s390x, qemu-devel
On Thu, Sep 17, 2026 at 10:25:32AM -0700, Farhan Ali wrote:
>
> On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
> > @@ -1303,11 +1380,22 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
> > pbdev = S390_PCI_DEVICE(dev);
> > pbdev->fid = 0;
> > QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
> > - g_hash_table_remove(s->zpci_table, &pbdev->idx);
> > + /*
> > + * If this QEMU is running a migrated guest, and was configured with
> > + * more zpci devices than the source QEMU, extra zpci devices could be
> > + * excluded from zpci_table and invisible to the guest and have
> > + * pbdev->idx values duplicating those of active devices. If the
> > + * pbdev being unplugged is such, the zpci_table entry for pbdev->idx
> > + * can find a different pbdev which we must not remove.
> > + */
> > + if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
> > + g_hash_table_remove(s->zpci_table, &pbdev->idx);
> > + }
>
> If we are migrating, won't we re-create the exact same state and devices on
> the target host? Is it possible that the target QEMU can have more devices
> than source?
It is expected that the source and dest QEMU processes have identical
virtual hardware configuration, and also no attempts should be made to
hot-add/remove devices while migration is running.
>
>
> > if (pbdev->dma_limit) {
> > s390_pci_end_dma_count(s, pbdev->dma_limit);
> > }
> > g_hash_table_destroy(pbdev->iotlb);
> > + s390_clear_zpci_migration_blocker(pbdev);
> > qdev_unrealize(dev);
> > }
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices
2026-09-17 17:35 ` Daniel P. Berrangé
@ 2026-09-21 15:12 ` Konstantin Shkolnyy
2026-09-21 15:19 ` Daniel P. Berrangé
0 siblings, 1 reply; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-21 15:12 UTC (permalink / raw)
To: Daniel P. Berrangé, Farhan Ali
Cc: mjrosato, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel
On 260917 12:35, Daniel P. Berrangé wrote:
> On Thu, Sep 17, 2026 at 10:25:32AM -0700, Farhan Ali wrote:
>>
>> On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
>>> @@ -1303,11 +1380,22 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
>>> pbdev = S390_PCI_DEVICE(dev);
>>> pbdev->fid = 0;
>>> QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
>>> - g_hash_table_remove(s->zpci_table, &pbdev->idx);
>>> + /*
>>> + * If this QEMU is running a migrated guest, and was configured with
>>> + * more zpci devices than the source QEMU, extra zpci devices could be
>>> + * excluded from zpci_table and invisible to the guest and have
>>> + * pbdev->idx values duplicating those of active devices. If the
>>> + * pbdev being unplugged is such, the zpci_table entry for pbdev->idx
>>> + * can find a different pbdev which we must not remove.
>>> + */
>>> + if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
>>> + g_hash_table_remove(s->zpci_table, &pbdev->idx);
>>> + }
>>
>> If we are migrating, won't we re-create the exact same state and devices on
>> the target host? Is it possible that the target QEMU can have more devices
>> than source?
>
> It is expected that the source and dest QEMU processes have identical
> virtual hardware configuration, and also no attempts should be made to
> hot-add/remove devices while migration is running.
Do you suggest to completely delete this hunk, or keep as "defensive
tactic" and correct the comment?
>
>>
>>
>>> if (pbdev->dma_limit) {
>>> s390_pci_end_dma_count(s, pbdev->dma_limit);
>>> }
>>> g_hash_table_destroy(pbdev->iotlb);
>>> + s390_clear_zpci_migration_blocker(pbdev);
>>> qdev_unrealize(dev);
>>> }
>>
>
> With regards,
> Daniel
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices
2026-09-21 15:12 ` Konstantin Shkolnyy
@ 2026-09-21 15:19 ` Daniel P. Berrangé
2026-09-21 16:53 ` Farhan Ali
0 siblings, 1 reply; 26+ messages in thread
From: Daniel P. Berrangé @ 2026-09-21 15:19 UTC (permalink / raw)
To: Konstantin Shkolnyy
Cc: Farhan Ali, mjrosato, farman, richard.henderson, iii, david,
cohuck, pasic, borntraeger, qemu-s390x, qemu-devel
On Mon, Sep 21, 2026 at 10:12:34AM -0500, Konstantin Shkolnyy wrote:
> On 260917 12:35, Daniel P. Berrangé wrote:
> > On Thu, Sep 17, 2026 at 10:25:32AM -0700, Farhan Ali wrote:
> > >
> > > On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
> > > > @@ -1303,11 +1380,22 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
> > > > pbdev = S390_PCI_DEVICE(dev);
> > > > pbdev->fid = 0;
> > > > QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
> > > > - g_hash_table_remove(s->zpci_table, &pbdev->idx);
> > > > + /*
> > > > + * If this QEMU is running a migrated guest, and was configured with
> > > > + * more zpci devices than the source QEMU, extra zpci devices could be
> > > > + * excluded from zpci_table and invisible to the guest and have
> > > > + * pbdev->idx values duplicating those of active devices. If the
> > > > + * pbdev being unplugged is such, the zpci_table entry for pbdev->idx
> > > > + * can find a different pbdev which we must not remove.
> > > > + */
> > > > + if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
> > > > + g_hash_table_remove(s->zpci_table, &pbdev->idx);
> > > > + }
> > >
> > > If we are migrating, won't we re-create the exact same state and devices on
> > > the target host? Is it possible that the target QEMU can have more devices
> > > than source?
> >
> > It is expected that the source and dest QEMU processes have identical
> > virtual hardware configuration, and also no attempts should be made to
> > hot-add/remove devices while migration is running.
>
> Do you suggest to completely delete this hunk, or keep as "defensive tactic"
> and correct the comment?
I don't think it is worth trying to add defensive checks, as if the
config is not identical, the code may well not even be reached as
vmstate parsing/loading typically fails during migration.
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices
2026-09-21 15:19 ` Daniel P. Berrangé
@ 2026-09-21 16:53 ` Farhan Ali
0 siblings, 0 replies; 26+ messages in thread
From: Farhan Ali @ 2026-09-21 16:53 UTC (permalink / raw)
To: Daniel P. Berrangé, Konstantin Shkolnyy
Cc: mjrosato, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel
On 9/21/2026 8:19 AM, Daniel P. Berrangé wrote:
> On Mon, Sep 21, 2026 at 10:12:34AM -0500, Konstantin Shkolnyy wrote:
>> On 260917 12:35, Daniel P. Berrangé wrote:
>>> On Thu, Sep 17, 2026 at 10:25:32AM -0700, Farhan Ali wrote:
>>>> On 9/11/2026 8:21 AM, Konstantin Shkolnyy wrote:
>>>>> @@ -1303,11 +1380,22 @@ static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
>>>>> pbdev = S390_PCI_DEVICE(dev);
>>>>> pbdev->fid = 0;
>>>>> QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
>>>>> - g_hash_table_remove(s->zpci_table, &pbdev->idx);
>>>>> + /*
>>>>> + * If this QEMU is running a migrated guest, and was configured with
>>>>> + * more zpci devices than the source QEMU, extra zpci devices could be
>>>>> + * excluded from zpci_table and invisible to the guest and have
>>>>> + * pbdev->idx values duplicating those of active devices. If the
>>>>> + * pbdev being unplugged is such, the zpci_table entry for pbdev->idx
>>>>> + * can find a different pbdev which we must not remove.
>>>>> + */
>>>>> + if (g_hash_table_lookup(s->zpci_table, &pbdev->idx) == pbdev) {
>>>>> + g_hash_table_remove(s->zpci_table, &pbdev->idx);
>>>>> + }
>>>> If we are migrating, won't we re-create the exact same state and devices on
>>>> the target host? Is it possible that the target QEMU can have more devices
>>>> than source?
>>> It is expected that the source and dest QEMU processes have identical
>>> virtual hardware configuration, and also no attempts should be made to
>>> hot-add/remove devices while migration is running.
>> Do you suggest to completely delete this hunk, or keep as "defensive tactic"
>> and correct the comment?
> I don't think it is worth trying to add defensive checks, as if the
> config is not identical, the code may well not even be reached as
> vmstate parsing/loading typically fails during migration.
>
>
> With regards,
> Daniel
I would agree to remove it, it was also a little confusing for me to
understand why we needed it.
Thanks
Farhan
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v8 16/16] s390x/pci: Create function to contain fmb_timer start
2026-09-11 15:21 [PATCH v8 00/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
` (14 preceding siblings ...)
2026-09-11 15:21 ` [PATCH v8 15/16] s390x/pci: Implement migration for emulated devices Konstantin Shkolnyy
@ 2026-09-11 15:21 ` Konstantin Shkolnyy
15 siblings, 0 replies; 26+ messages in thread
From: Konstantin Shkolnyy @ 2026-09-11 15:21 UTC (permalink / raw)
To: mjrosato
Cc: alifm, farman, richard.henderson, iii, david, cohuck, pasic,
borntraeger, qemu-s390x, qemu-devel, Konstantin Shkolnyy
fmb_timer is now started in 3 different places. The new function will
encapsulate that to make sure mui is added in all cases.
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Konstantin Shkolnyy <kshk@linux.ibm.com>
---
hw/s390x/s390-pci-bus.c | 5 ++---
hw/s390x/s390-pci-inst.c | 14 ++++++++++----
include/hw/s390x/s390-pci-inst.h | 1 +
3 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
index c16941ace6..452264976d 100644
--- a/hw/s390x/s390-pci-bus.c
+++ b/hw/s390x/s390-pci-bus.c
@@ -1864,9 +1864,8 @@ static bool s390_pci_device_post_load_errp(void *opaque, int version_id,
}
pbdev->fmb_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
fmb_update, pbdev);
- timer_mod(pbdev->fmb_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
- pbdev->pci_group->zpci_group.mui);
+ s390_pci_schedule_fmb_timer(pbdev,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
}
return true;
}
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c
index 9b701b4dff..a559b1dcec 100644
--- a/hw/s390x/s390-pci-inst.c
+++ b/hw/s390x/s390-pci-inst.c
@@ -1135,9 +1135,16 @@ static int fmb_do_update(S390PCIBusDevice *pbdev, int offset, uint64_t val,
return ret;
}
+void s390_pci_schedule_fmb_timer(S390PCIBusDevice *pbdev, uint64_t start)
+{
+ timer_mod(pbdev->fmb_timer, start + pbdev->pci_group->zpci_group.mui);
+}
+
void fmb_update(void *opaque)
{
S390PCIBusDevice *pbdev = opaque;
+
+ /* Must be read before updating U bit */
int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
int i;
@@ -1174,7 +1181,7 @@ void fmb_update(void *opaque)
sizeof(pbdev->fmb.last_update))) {
return;
}
- timer_mod(pbdev->fmb_timer, t + pbdev->pci_group->zpci_group.mui);
+ s390_pci_schedule_fmb_timer(pbdev, t);
}
static int mpcifc_reg_int_interp(S390PCIBusDevice *pbdev, ZpciFib *fib)
@@ -1367,9 +1374,8 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
timer_del(pbdev->fmb_timer);
}
pbdev->fmb_addr = fmb_addr;
- timer_mod(pbdev->fmb_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
- pbdev->pci_group->zpci_group.mui);
+ s390_pci_schedule_fmb_timer(pbdev,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL));
break;
}
default:
diff --git a/include/hw/s390x/s390-pci-inst.h b/include/hw/s390x/s390-pci-inst.h
index 873773f621..86d75134b9 100644
--- a/include/hw/s390x/s390-pci-inst.h
+++ b/include/hw/s390x/s390-pci-inst.h
@@ -114,6 +114,7 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar,
uintptr_t ra);
void fmb_timer_free(S390PCIBusDevice *pbdev);
void fmb_update(void *opaque);
+void s390_pci_schedule_fmb_timer(S390PCIBusDevice *pbdev, uint64_t start);
uint32_t s390_pci_update_iotlb(S390PCIBusDevice *pbdev, S390IOTLBEntry *entry);
#define ZPCI_IO_BAR_MIN 0
--
2.34.1
^ permalink raw reply related [flat|nested] 26+ messages in thread