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); + 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) -- 2.54.0.1136.gdb2ca164c4-goog
