On 2026/06/11 11:30, Josh Hilke wrote:
Introduce register offsets and bitmask constants for the Transmit Rate
Limiter (TRL). Add the IGBTrlTimer structure and corresponding fields to
IGBCore to track TRL state (timers, throttling status, and target rates)
per queue. Initialize and free these timers during the device
realize/uninit lifecycle, and reset them on device reset.

The added state should be included in VMState.
Please see: docs/devel/migration/main.rst

Regards,
Akihiko Odaki


Signed-off-by: Josh Hilke <[email protected]>
---
  hw/net/igb_common.h |  1 +
  hw/net/igb_core.c   | 17 +++++++++++++++++
  hw/net/igb_core.h   | 11 +++++++++++
  hw/net/igb_regs.h   | 11 +++++++++++
  4 files changed, 40 insertions(+)

diff --git a/hw/net/igb_common.h b/hw/net/igb_common.h
index b316a5b..8c0a0cf 100644
--- a/hw/net/igb_common.h
+++ b/hw/net/igb_common.h
@@ -148,6 +148,7 @@ enum {
      defreg(MTA_A),
defreg(VTIVAR), defreg(VTIVAR_MISC),
+    defreg(TRLDQSEL), defreg(TRLRC),
  };
uint64_t igb_mmio_read(void *opaque, hwaddr addr, unsigned size);
diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c
index 45d8fd7..64f21c1 100644
--- a/hw/net/igb_core.c
+++ b/hw/net/igb_core.c
@@ -4282,6 +4282,11 @@ igb_autoneg_resume(IGBCore *core)
      }
  }
+static void igb_trl_timer_cb(void *opaque)
+{
+    /* Stub */
+}
+
  void
  igb_core_pci_realize(IGBCore        *core,
                       const uint16_t *eeprom_templ,
@@ -4296,6 +4301,11 @@ igb_core_pci_realize(IGBCore        *core,
for (i = 0; i < IGB_NUM_QUEUES; i++) {
          net_tx_pkt_init(&core->tx[i].tx_pkt, E1000E_MAX_TX_FRAGS);
+        core->trl_timer[i].timer =
+            timer_new_ns(QEMU_CLOCK_VIRTUAL, igb_trl_timer_cb,
+                         &core->trl_timer[i]);
+        core->trl_timer[i].core = core;
+        core->trl_timer[i].queue_idx = i;
      }
net_rx_pkt_init(&core->rx_pkt);
@@ -4319,6 +4329,7 @@ igb_core_pci_uninit(IGBCore *core)
for (i = 0; i < IGB_NUM_QUEUES; i++) {
          net_tx_pkt_uninit(core->tx[i].tx_pkt);
+        timer_free(core->trl_timer[i].timer);
      }
net_rx_pkt_uninit(core->rx_pkt);
@@ -4469,6 +4480,12 @@ static void igb_reset(IGBCore *core, bool sw)
igb_intrmgr_reset(core); + for (i = 0; i < IGB_NUM_QUEUES; i++) {
+        timer_del(core->trl_timer[i].timer);
+        core->trl_throttled[i] = false;
+        core->trl_target_rate[i] = 0;
+    }
+
      memset(core->phy, 0, sizeof core->phy);
      memcpy(core->phy, igb_phy_reg_init, sizeof igb_phy_reg_init);
diff --git a/hw/net/igb_core.h b/hw/net/igb_core.h
index d70b54e..ffd45ed 100644
--- a/hw/net/igb_core.h
+++ b/hw/net/igb_core.h
@@ -63,6 +63,12 @@ typedef struct IGBIntrDelayTimer_st {
      IGBCore *core;
  } IGBIntrDelayTimer;
+typedef struct IGBTrlTimer_st {
+    QEMUTimer *timer;
+    IGBCore *core;
+    int queue_idx;
+} IGBTrlTimer;
+
  struct IGBCore {
      uint32_t mac[E1000E_MAC_SIZE];
      uint16_t phy[MAX_PHY_REG_ADDRESS + 1];
@@ -99,6 +105,11 @@ struct IGBCore {
      void (*owner_start_recv)(PCIDevice *d);
int64_t timadj;
+
+    /* Transmit Rate Limiting */
+    IGBTrlTimer trl_timer[IGB_NUM_QUEUES];
+    bool trl_throttled[IGB_NUM_QUEUES];
+    uint64_t trl_target_rate[IGB_NUM_QUEUES]; /* in bytes per second */
  };
void
diff --git a/hw/net/igb_regs.h b/hw/net/igb_regs.h
index 4dc4c31..f7895ef 100644
--- a/hw/net/igb_regs.h
+++ b/hw/net/igb_regs.h
@@ -718,4 +718,15 @@ static inline uint8_t igb_ivar_entry_tx(uint8_t i)
      return i < 8 ? i * 4 + 1 : (i - 8) * 4 + 3;
  }
+/* Transmit Rate Limiting */
+#define E1000_TRLDQSEL 0x03604
+#define E1000_TRLRC    0x036B0
+
+#define E1000_TRLRC_RS_ENA      BIT(31)
+#define E1000_TRLRC_RF_DEC_MASK 0x3FFF
+#define E1000_TRLRC_RF_INT_MASK 0x3FF
+
+/* 1 Gbps link rate in bytes per second (1,000,000,000 bits/s / 8) */
+#define E1000_LINK_RATE_1GBPS 125000000ULL
+
  #endif


Reply via email to