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


Reply via email to