On 2026/07/27 14:39, Cédric Le Goater wrote:
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.

The patch message describes the problem this patch intends to solve, but it does not explain why it should be handled with the device emulation code. My intuition is that this is a responsibility of a high-level management layer on L1.


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);

The target-hardware address is also zeroed rather than populated like QEMU’s existing RARP builder.

+
+    qemu_send_packet_raw(qemu_get_queue(core->owner_nic), buf, sizeof(buf));

RARP is injected directly into the PF backend, bypassing normal VF TX VLAN insertion. A VLAN-configured VF therefore announces itself untagged, so the bridge may learn the wrong VLAN or discard it.

Regards,
Akihiko Odaki

+}
+
  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


Reply via email to