On 2026/06/17 7:35, 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 | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 71 insertions(+), 2 deletions(-)
diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c
index b7f880e..7f14564 100644
--- a/hw/net/igb_core.c
+++ b/hw/net/igb_core.c
@@ -96,6 +96,8 @@ igb_receive_internal(IGBCore *core, const struct iovec *iov,
int iovcnt,
static void igb_raise_interrupts(IGBCore *core, size_t index, uint32_t causes);
static void igb_reset(IGBCore *core, bool sw);
+static uint64_t igb_trl_get_target_rate(const IGBTrlQueue *q);
+
static inline void
igb_raise_legacy_irq(IGBCore *core)
@@ -875,6 +877,16 @@ igb_tx_enabled(IGBCore *core, const E1000ERingInfo *txi)
(core->mac[TXDCTL0 + (qn * 16)] & E1000_TXDCTL_QUEUE_ENABLE);
}
+static uint64_t igb_trl_calculate_delay(uint64_t target_rate,
+ size_t bytes_sent)
+{
+ uint64_t low, high;
+
+ mulu64(&low, &high, bytes_sent, NANOSECONDS_PER_SECOND);
+ divu128(&low, &high, target_rate);
What Philippe suggested is to use muldiv64(), but this is more
complicated because of uint64_t. I think it will be simpler and more
accurate if you merge igb_trl_get_target_rate() and this function.
Regards,
Akihiko Odaki
+ return low;
+}
+
static void
igb_start_xmit(IGBCore *core, const IGB_TxRing *txr)
{
@@ -883,12 +895,19 @@ 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 = 0;
+ uint64_t target_rate;
+ uint64_t delay_ns;
if (!igb_tx_enabled(core, txi)) {
trace_e1000e_tx_disabled();
return;
}
+ if (timer_pending(core->trl[txi->idx].timer)) {
+ return;
+ }
+
d = pcie_sriov_get_vf_at_index(core->owner, txi->idx % 8);
if (!d) {
d = core->owner;
@@ -902,9 +921,19 @@ 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);
+
+ target_rate = igb_trl_get_target_rate(&core->trl[txi->idx]);
+ if (target_rate > 0) {
+ delay_ns = igb_trl_calculate_delay(target_rate, bytes_sent);
+ if (delay_ns > 0) {
+ timer_mod(core->trl[txi->idx].timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + delay_ns);
+ break;
+ }
+ }
}
if (eic) {
@@ -4330,15 +4359,55 @@ igb_autoneg_resume(IGBCore *core)
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 500);
}
}
+static uint32_t igb_trl_get_scaled_rate_factor(uint32_t trlrc)
+{
+ /*
+ * The TRLRC register stores the Rate Factor in 10.14 fixed-point format:
+ * - Bits 13:0: Fractional part
+ * - Bits 23:14: Integer part
+ *
+ * Combined, the lower 24 bits of the register (23:0) represent the
+ * concatenated 24-bit scaled Rate Factor directly.
+ */
+ return extract32(trlrc, 0, 24);
+}
bool igb_trl_enabled(const IGBTrlQueue *q)
{
return q->trlrc & E1000_TRLRC_RS_ENA;
}
+static uint64_t igb_trl_get_target_rate(const IGBTrlQueue *q)
+{
+ const uint32_t rf_scaled = igb_trl_get_scaled_rate_factor(q->trlrc);
+
+ if (!igb_trl_enabled(q)) {
+ return 0;
+ }
+
+ if (rf_scaled == 0) {
+ return 0;
+ }
+
+ /*
+ * The rate factor is a fixed-point number with a 14-bit fractional part:
+ * Rate Factor = rf_scaled / 2^14
+ *
+ * The target rate is:
+ * Target Rate = Link Rate / Rate Factor
+ * = Link Rate / (rf_scaled / 2^14)
+ * = (Link Rate * 2^14) / rf_scaled
+ */
+ return (E1000_LINK_RATE_1GBPS << 14) / rf_scaled;
+}
+
static void igb_trl_timer_cb(void *opaque)
{
- /* Stub */
+ IGBTrlQueue *trl = opaque;
+ IGB_TxRing txr;
+
+ igb_tx_ring_init(trl->core, &txr, trl->qidx);
+ igb_start_xmit(trl->core, &txr);
}
static void igb_trl_init(IGBCore *core)