On 2026/06/16 7:18, Josh Hilke wrote:
Implement write handlers for TLDQSEL and TRLRC registers to allow the
guest to configure Transmit Rate Limiting. Translate the register Rate
Factor into a physical target rate (bytes/sec) per queue. Disable TRL
and clear timers when the link status changes or when TRL is disabled by
the guest.
Signed-off-by: Josh Hilke <[email protected]>
---
hw/net/igb_core.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c
index c94e8d3..dea8136 100644
--- a/hw/net/igb_core.c
+++ b/hw/net/igb_core.c
@@ -2137,11 +2137,27 @@ igb_set_phy_ctrl(IGBCore *core, uint16_t val)
}
}
+static void igb_trl_disable_queue(IGBCore *core, int qidx)
+{
+ core->trl[qidx].trlrc = 0;
+
+ timer_del(core->trl[qidx].timer);
+}
+
+static void igb_trl_disable(IGBCore *core)
+{
+ for (int i = 0; i < IGB_NUM_QUEUES; i++) {
+ igb_trl_disable_queue(core, i);
+ }
+}
+
void igb_core_set_link_status(IGBCore *core)
{
NetClientState *nc = qemu_get_queue(core->owner_nic);
uint32_t old_status = core->mac[STATUS];
+ igb_trl_disable(core);
+
trace_e1000e_link_status_changed(nc->link_down ? false : true);
if (nc->link_down) {
@@ -2624,6 +2640,42 @@ static void igb_set_vtivar(IGBCore *core, int index,
uint32_t val)
*/
}
+static uint32_t igb_get_trlrc(IGBCore *core, int index)
+{
+ int qidx = core->mac[TRLDQSEL];
+
+ return core->trl[qidx].trlrc;
+}
+
+static void igb_set_trldqsel(IGBCore *core, int index, uint32_t val)
+{
+ core->mac[index] = val & 0xF;
+}
+
+static void igb_set_trlrc(IGBCore *core, int index, uint32_t val)
+{
+ uint64_t rf_scaled;
+ int qidx;
+
+ qidx = core->mac[TRLDQSEL];
+ core->trl[qidx].trlrc = val;
+
+ if (!igb_trl_enabled(val)) {
+ goto disable;
+ }
+
+ rf_scaled = igb_trl_get_scaled_rate_factor(val);
+
+ /* Zero is not a valid rate factor, treat as disabled */
+ if (rf_scaled == 0) {
+ goto disable;
+ }
+ return;
+
+disable:
+ igb_trl_disable_queue(core, qidx);
This behavior of overwriting TRLRC and dropping the pending work is not
specified in the datasheet and extraneous.
Regards,
Akihiko Odaki
+}
+
static inline void
igb_autoneg_timer(void *opaque)
{
@@ -3679,6 +3731,7 @@ static const readops igb_macreg_readops[] = {
[RQDPC15] = igb_mac_read_clr4,
[VTIVAR ... VTIVAR + 7] = igb_mac_readreg,
[VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_readreg,
+ [TRLRC] = igb_get_trlrc,
};
enum { IGB_NREADOPS = ARRAY_SIZE(igb_macreg_readops) };
@@ -4127,7 +4180,9 @@ static const writeops igb_macreg_writeops[] = {
[PVTEICR6] = igb_set_vteicr,
[PVTEICR7] = igb_set_vteicr,
[VTIVAR ... VTIVAR + 7] = igb_set_vtivar,
- [VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_writereg
+ [VTIVAR_MISC ... VTIVAR_MISC + 7] = igb_mac_writereg,
+ [TRLDQSEL] = igb_set_trldqsel,
+ [TRLRC] = igb_set_trlrc
};
enum { IGB_NWRITEOPS = ARRAY_SIZE(igb_macreg_writeops) };