On 2026/06/11 11:30, Josh Hilke wrote:
Integrate throttling logic into the transmission path. In
`igb_start_xmit`, check if the queue is currently throttled.  Accumulate
bytes sent, calculate the transmission delay based on the configured
target rate, and if a throttle is triggered, set the virtual timer, mark
the queue as throttled, and defer remaining packets.  Implement the
timer callback to clear the throttled state and resume transmission when
the timer expires.

Signed-off-by: Josh Hilke <[email protected]>
---
  hw/net/igb_core.c | 37 +++++++++++++++++++++++++++++++++++--
  1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c
index 9ed0e78..b8c0500 100644
--- a/hw/net/igb_core.c
+++ b/hw/net/igb_core.c
@@ -875,6 +875,14 @@ igb_tx_enabled(IGBCore *core, const E1000ERingInfo *txi)
          (core->mac[TXDCTL0 + (qn * 16)] & E1000_TXDCTL_QUEUE_ENABLE);
  }
+static uint64_t igb_trl_calculate_delay(IGBCore *core, int qidx,
+                                        size_t bytes_sent)
+{
+    uint64_t ns_per_sec = 1000000000ULL;

Please use NANOSECONDS_PER_SECOND.

+
+    return (uint64_t)bytes_sent * ns_per_sec / core->trl_target_rate[qidx];
+}
+
  static void
  igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
  {
@@ -883,17 +891,23 @@ igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
      union e1000_adv_tx_desc desc;
      const E1000ERingInfo *txi = txr->i;
      uint32_t eic = 0;
+    size_t bytes_sent;
if (!igb_tx_enabled(core, txi)) {
          trace_e1000e_tx_disabled();
          return;
      }
+ if (core->trl_throttled[txi->idx]) {
+        return;
+    }
+
      d = pcie_sriov_get_vf_at_index(core->owner, txi->idx % 8);
      if (!d) {
          d = core->owner;
      }
+ bytes_sent = 0;
      while (!igb_ring_empty(core, txi)) {
          base = igb_ring_head_descr(core, txi);
@@ -902,9 +916,20 @@ igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
          trace_e1000e_tx_descr((void *)(intptr_t)desc.read.buffer_addr,
                                desc.read.cmd_type_len, desc.wb.status);
- igb_process_tx_desc(core, d, txr->tx, &desc, txi->idx);
+        bytes_sent += igb_process_tx_desc(core, d, txr->tx, &desc, txi->idx);
          igb_ring_advance(core, txi, 1);
          eic |= igb_txdesc_writeback(core, base, &desc, txi);
+
+        if (core->trl_target_rate[txi->idx] > 0) {
+            uint64_t delay_ns =
+                igb_trl_calculate_delay(core, txi->idx, bytes_sent);
+            if (delay_ns > 0) {
+                core->trl_throttled[txi->idx] = true;

I think you can use timer_pending() instead of having a separate field, trl_throttled.

Regards,
Akihiko Odaki

+                timer_mod(core->trl_timer[txi->idx].timer,
+                          qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + delay_ns);
+                break;
+            }
+        }
      }
if (eic) {
@@ -4358,7 +4383,15 @@ igb_autoneg_resume(IGBCore *core)
static void igb_trl_timer_cb(void *opaque)
  {
-    /* Stub */
+    IGBTrlTimer *trl_timer = opaque;
+    IGBCore *core = trl_timer->core;
+    int qidx = trl_timer->queue_idx;
+    IGB_TxRing txr;
+
+    core->trl_throttled[qidx] = false;
+
+    igb_tx_ring_init(core, &txr, qidx);
+    igb_start_xmit(core, &txr);
  }
void


Reply via email to