On 9/8/26 10:10, Akihiko Odaki wrote:
On 2026/09/03 4:20, Cédric Le Goater wrote:
After restoring per-VF register state, propagate the VF's PVT shadow
values back into the PF's aggregate EIMS/EIAC/EIAM registers and
re-apply the VTIVAR interrupt vector routing to the shared IVAR0.
AI-used-for: analysis, code (prototype)
Signed-off-by: Cédric Le Goater <[email protected]>
---
hw/net/igb_core.h | 3 ++
hw/net/igb_core.c | 66 ++++++++++++++++++++++++++++++++++++++++++
hw/net/igb_migration.c | 7 +++++
3 files changed, 76 insertions(+)
diff --git a/hw/net/igb_core.h b/hw/net/igb_core.h
index 60724e2824ab..22e10e4e6d0b 100644
--- a/hw/net/igb_core.h
+++ b/hw/net/igb_core.h
@@ -145,4 +145,7 @@ igb_start_recv(IGBCore *core);
IGBCore *igb_pf_get_core(void *pf);
+void igb_core_vf_propagate_irqs(IGBCore *core, uint16_t vfn);
+void igb_core_vf_propagate_ivar(IGBCore *core, uint16_t vfn);
+
#endif
diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c
index 2a4883907353..01745fe756d0 100644
--- a/hw/net/igb_core.c
+++ b/hw/net/igb_core.c
@@ -4552,3 +4552,69 @@ igb_core_post_load(IGBCore *core)
return 0;
}
+
+/*
+ * Propagate VF interrupt state to PF aggregates after loading VF
+ * registers. The load path writes directly to mac[] bypassing the
+ * register handlers that OR VF bits into EIMS/EIAC/EIAM. Also clear
+ * stale VF bits in EICR that may have been set by packets arriving
+ * between PF vmstate restore and VF state load.
+ */
+void igb_core_vf_propagate_irqs(IGBCore *core, uint16_t vfn)
+{
+ uint32_t shift = 22 - vfn * IGBVF_MSIX_VEC_NUM;
+ uint32_t vf_mask = 0x7 << shift;
+ uint32_t pvt_idx;
+
+ core->mac[EIMS] &= ~vf_mask;
Restore the effective interrupt mask rather than the last EIMS write.
This clears the destination VF mask and copies PVTEIMS, but that shadow records
only the last write: igb_set_vteims() assigns it before applying set-bit
semantics to aggregate EIMS. For example, source writes EIMS=1, then EIMS=2,
leave effective mask 3 and shadow 2; migration restores 2, suppressing vector 0
until explicitly enabled again. Conversely, EIMC updates aggregate EIMS without
updating PVTEIMS, so migration reenables explicitly masked vectors. Patch 7
repeats this fixup on unquiesce without repairing the bookkeeping.
ok. I guess we can save and restore the VF's effective mask bits directly
and drop the fixup then. Same for EIAC/EIAM. It should simplify the flow.
How's that ?
Thanks
C.
Regards,
Akihiko Odaki
+ pvt_idx = PVTEIMS0 + vfn * 0x40;
+ core->mac[EIMS] |= (core->mac[pvt_idx] & 0x7) << shift;
+
+ core->mac[EIAC] &= ~vf_mask;
+ pvt_idx = PVTEIAC0 + vfn * 0x40;
+ core->mac[EIAC] |= (core->mac[pvt_idx] & 0x7) << shift;
+
+ core->mac[EIAM] &= ~vf_mask;
+ pvt_idx = PVTEIAM0 + vfn * 0x40;
+ core->mac[EIAM] |= (core->mac[pvt_idx] & 0x7) << shift;
+
+ core->mac[EICR] &= ~vf_mask;
+}
+
+/*
+ * Re-apply VTIVAR -> IVAR0 interrupt routing. The L1 PF driver
+ * may have overwritten the shared IVAR0 entries with its own
+ * queue routing after L0 vmstate restore.
+ */
+void igb_core_vf_propagate_ivar(IGBCore *core, uint16_t vfn)
+{
+ uint32_t vtivar = core->mac[VTIVAR + vfn];
+ int n;
+ uint8_t ent;
+ uint32_t mask;
+
+ n = igb_ivar_entry_rx(vfn);
+ mask = 0xffU << (8 * (n % 4));
+ if (vtivar & E1000_IVAR_VALID) {
+ ent = E1000_IVAR_VALID |
+ (24 - vfn * IGBVF_MSIX_VEC_NUM - (2 - (vtivar & 0x7)));
+ core->mac[IVAR0 + n / 4] =
+ (core->mac[IVAR0 + n / 4] & ~mask) |
+ ((uint32_t)ent << (8 * (n % 4)));
+ } else {
+ core->mac[IVAR0 + n / 4] &= ~mask;
+ }
+
+ n = igb_ivar_entry_tx(vfn);
+ mask = 0xffU << (8 * (n % 4));
+ ent = vtivar >> 8;
+ if (ent & E1000_IVAR_VALID) {
+ ent = E1000_IVAR_VALID |
+ (24 - vfn * IGBVF_MSIX_VEC_NUM - (2 - (ent & 0x7)));
+ core->mac[IVAR0 + n / 4] =
+ (core->mac[IVAR0 + n / 4] & ~mask) |
+ ((uint32_t)ent << (8 * (n % 4)));
+ } else {
+ core->mac[IVAR0 + n / 4] &= ~mask;
+ }
+}
diff --git a/hw/net/igb_migration.c b/hw/net/igb_migration.c
index c34035974620..af7257ccc4fd 100644
--- a/hw/net/igb_migration.c
+++ b/hw/net/igb_migration.c
@@ -384,12 +384,19 @@ static int igb_core_vf_load_state(IgbVfState *s, const
void *buf, size_t size)
static int igbvf_mig_load(IgbVfState *s, const void *buf, size_t size)
{
int ret;
+ IGBCore *core = igbvf_get_core(s);
ret = igb_core_vf_load_state(s, buf, size);
if (ret < 0) {
return ret;
}
+ /*
+ * Post-load: sync VF interrupt and routing state to PF aggregates
+ */
+ igb_core_vf_propagate_irqs(core, s->vfn);
+ igb_core_vf_propagate_ivar(core, s->vfn);
+
return 0;
}