After VF migration the L0 bridge FDB (Forwarding Database) still maps the VF's MAC to the old tap port, causing a ~30 s connectivity stall while peer ARP caches expire.
Add igb_core_vf_get_mac() to look up a VF's MAC address from the PF's Receive Address registers, and use it to send a RARP broadcast from the PF's network backend during VF unquiesce so the bridge relearns the correct port immediately. Assisted-by: Claude Signed-off-by: Cédric Le Goater <[email protected]> --- hw/net/igb_core.h | 1 + hw/net/igb_core.c | 28 ++++++++++++++++++++++++++++ hw/net/igb_migration.c | 33 +++++++++++++++++++++++++++++++++ hw/net/trace-events | 2 ++ 4 files changed, 64 insertions(+) diff --git a/hw/net/igb_core.h b/hw/net/igb_core.h index 3db520048732..07b4270708c4 100644 --- a/hw/net/igb_core.h +++ b/hw/net/igb_core.h @@ -149,4 +149,5 @@ IGBCore *igb_pf_get_core(void *pf); void igb_core_vf_propagate_irqs(IGBCore *core, uint16_t vfn); void igb_core_vf_rearm_irqs(IGBCore *core, uint16_t vfn); void igb_core_vf_propagate_ivar(IGBCore *core, uint16_t vfn); +bool igb_core_vf_get_mac(IGBCore *core, uint16_t vfn, uint8_t *mac); #endif diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c index 1621ee6602d5..281f3a474392 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -4634,3 +4634,31 @@ void igb_core_vf_propagate_ivar(IGBCore *core, uint16_t vfn) ((uint32_t)ent << (8 * (n % 4))); } } + +bool igb_core_vf_get_mac(IGBCore *core, uint16_t vfn, uint8_t *mac) +{ + uint32_t vf_pool_bit = E1000_RAH_POOL_1 << vfn; + static const struct { + uint32_t base; + int count; + } ra_banks[] = { + { RA, 16 }, + { RA2, 8 }, + }; + int i, j; + + for (i = 0; i < ARRAY_SIZE(ra_banks); i++) { + for (j = 0; j < ra_banks[i].count; j++) { + uint32_t ral_off = ra_banks[i].base + j * 2; + uint32_t rah_off = ra_banks[i].base + j * 2 + 1; + uint32_t rah_val = core->mac[rah_off]; + + if ((rah_val & E1000_RAH_AV) && (rah_val & vf_pool_bit)) { + stl_le_p(mac, core->mac[ral_off]); + stw_le_p(mac + 4, rah_val & 0xffff); + return true; + } + } + } + return false; +} diff --git a/hw/net/igb_migration.c b/hw/net/igb_migration.c index a0b4a52c865f..fce661f68e3d 100644 --- a/hw/net/igb_migration.c +++ b/hw/net/igb_migration.c @@ -609,6 +609,36 @@ static void igb_core_vf_quiesce(IgbVfState *s) trace_igbvf_mig_quiesce(s->vfn, core->mac[VFRE], core->mac[VFTE]); } +/* + * Send a RARP broadcast so the network bridge relearns which port + * carries this VF's MAC after migration. + */ +static void igb_core_vf_send_rarp(IGBCore *core, uint16_t vfn) +{ + uint8_t mac[ETH_ALEN]; + uint8_t buf[60]; + + if (!igb_core_vf_get_mac(core, vfn, mac)) { + trace_igbvf_mig_no_mac(vfn); + return; + } + + trace_igbvf_mig_send_rarp(vfn, mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); + + memset(buf, 0xff, ETH_ALEN); + memcpy(buf + 6, mac, ETH_ALEN); + stw_be_p(buf + 12, 0x8035); /* ETH_P_RARP */ + stw_be_p(buf + 14, 1); /* hw addr space: ethernet */ + stw_be_p(buf + 16, ETH_P_IP); /* protocol addr space */ + buf[18] = 6; buf[19] = 4; /* hw/proto addr lengths */ + stw_be_p(buf + 20, 3); /* opcode: RARP request */ + memcpy(buf + 22, mac, ETH_ALEN); + memset(buf + 28, 0, 32); + + qemu_send_packet_raw(qemu_get_queue(core->owner_nic), buf, sizeof(buf)); +} + static void igb_core_vf_unquiesce(IgbVfState *s) { IgbVfMigState *ms = &s->mig; @@ -632,6 +662,9 @@ static void igb_core_vf_unquiesce(IgbVfState *s) if (re) { igb_core_vf_rearm_irqs(core, s->vfn); } + + /* TODO : RARP should be sent only if resumed */ + igb_core_vf_send_rarp(core, s->vfn); } /* ================================================================ diff --git a/hw/net/trace-events b/hw/net/trace-events index f4940e3e176d..1e38d9ab3697 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -312,6 +312,8 @@ igb_core_dirty_track_dma(int vfn, uint64_t addr, uint64_t len) "VF%d: dirty DMA igb_core_dirty_track_dma_drop(int vfn, uint64_t addr, uint64_t len) "VF%d: dirty DMA dropped addr=0x%"PRIx64" len=%"PRIu64" no matching range" igbvf_mig_quiesce(uint16_t vfn, uint32_t vfre, uint32_t vfte) "VF%u: quiesce VFRE=0x%x VFTE=0x%x" igbvf_mig_unquiesce(uint16_t vfn, uint32_t vfre, uint32_t vfte) "VF%u: unquiesce VFRE=0x%x VFTE=0x%x" +igbvf_mig_no_mac(uint16_t vfn) "VF%u: no MAC address found, skipping RARP" +igbvf_mig_send_rarp(uint16_t vfn, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "VF%u: RARP %02x:%02x:%02x:%02x:%02x:%02x" # spapr_llan.c spapr_vlan_get_rx_bd_from_pool_found(int pool, int32_t count, uint32_t rx_bufs) "pool=%d count=%"PRId32" rxbufs=%"PRIu32 -- 2.55.0
