From 17a480ce48bc448bace89dde0c4c651668362cf7 Mon Sep 17 00:00:00 2001 From: 8hodgsonkh Date: Tue, 24 Feb 2026 19:38:08 +0000 Subject: [PATCH 1/2] atlantic: add NET_DIM (Dynamic Interrupt Moderation) support --- 6.19/0008-atlantic-dim.patch | 225 +++++++++++++++++++++++++++++++++++ 7.0/0008-atlantic-dim.patch | 225 +++++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+) create mode 100644 6.19/0008-atlantic-dim.patch create mode 100644 7.0/0008-atlantic-dim.patch diff --git a/6.19/0008-atlantic-dim.patch b/6.19/0008-atlantic-dim.patch new file mode 100644 index 00000000..e8f4eb91 --- /dev/null +++ b/6.19/0008-atlantic-dim.patch @@ -0,0 +1,225 @@ +--- a/drivers/net/ethernet/aquantia/Kconfig 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/Kconfig 2026-02-23 01:33:03.716143177 +0000 +@@ -20,6 +20,7 @@ + tristate "aQuantia AQtion(tm) Support" + depends on PCI + depends on MACSEC || MACSEC=n ++ select NET_DIM + help + This enables the support for the aQuantia AQtion(tm) Ethernet card. + +--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h 2026-02-23 01:33:13.450057021 +0000 +@@ -12,6 +12,7 @@ + + #include + #include ++#include + #include + + #include "aq_common.h" +@@ -49,6 +50,8 @@ + u32 itr; + u16 rx_itr; + u16 tx_itr; ++ bool is_rx_adaptive; ++ bool is_tx_adaptive; + u32 rxpageorder; + u32 num_rss_queues; + u32 mtu; +@@ -128,6 +131,10 @@ + atomic_t flags; + u32 msg_enable; + struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX]; ++ /* DIM (Dynamic Interrupt Moderation) state */ ++ struct dim rx_dim; ++ struct dim tx_dim; ++ u16 dim_event_ctr; + struct aq_ring_s *aq_ring_tx[AQ_HW_QUEUES_MAX]; + struct aq_hw_s *aq_hw; + struct bpf_prog *xdp_prog; +@@ -218,4 +225,7 @@ + const u32 max_rate); + int aq_nic_setup_tc_min_rate(struct aq_nic_s *self, const unsigned int tc, + const u32 min_rate); ++ ++void aq_nic_dim_update(struct aq_nic_s *self); ++ + #endif /* AQ_NIC_H */ +--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-23 01:33:44.071788263 +0000 +@@ -18,6 +18,7 @@ + #include "aq_ptp.h" + #include "aq_filters.h" + ++#include + #include + #include + #include +@@ -65,6 +66,86 @@ + rss_params->indirection_table[i] = i & (num_rss_queues - 1); + } + ++/* ---- DIM (Dynamic Interrupt Moderation) ---- */ ++ ++static void aq_nic_rx_dim_work(struct work_struct *work) ++{ ++ struct dim *dim = container_of(work, struct dim, work); ++ struct aq_nic_s *nic = container_of(dim, struct aq_nic_s, rx_dim); ++ struct dim_cq_moder moder; ++ ++ moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); ++ ++ /* Clamp to hardware max (0x1FF * 2 = 1022 usec) */ ++ if (moder.usec > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) ++ moder.usec = AQ_CFG_INTERRUPT_MODERATION_USEC_MAX; ++ ++ nic->aq_nic_cfg.rx_itr = moder.usec; ++ nic->aq_nic_cfg.itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ ++ aq_nic_update_interrupt_moderation_settings(nic); ++ ++ dim->state = DIM_START_MEASURE; ++} ++ ++static void aq_nic_tx_dim_work(struct work_struct *work) ++{ ++ struct dim *dim = container_of(work, struct dim, work); ++ struct aq_nic_s *nic = container_of(dim, struct aq_nic_s, tx_dim); ++ struct dim_cq_moder moder; ++ ++ moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix); ++ ++ if (moder.usec > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) ++ moder.usec = AQ_CFG_INTERRUPT_MODERATION_USEC_MAX; ++ ++ nic->aq_nic_cfg.tx_itr = moder.usec; ++ nic->aq_nic_cfg.itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ ++ aq_nic_update_interrupt_moderation_settings(nic); ++ ++ dim->state = DIM_START_MEASURE; ++} ++ ++static void aq_nic_dim_init(struct aq_nic_s *self) ++{ ++ INIT_WORK(&self->rx_dim.work, aq_nic_rx_dim_work); ++ self->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; ++ self->rx_dim.profile_ix = 1; /* start at moderate profile */ ++ ++ INIT_WORK(&self->tx_dim.work, aq_nic_tx_dim_work); ++ self->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; ++ self->tx_dim.profile_ix = 1; ++ ++ self->dim_event_ctr = 0; ++} ++ ++void aq_nic_dim_update(struct aq_nic_s *self) ++{ ++ struct dim_sample sample = {}; ++ ++ self->dim_event_ctr++; ++ ++ /* Use hardware-level DMA packet/byte counters for global view */ ++ dim_update_sample(self->dim_event_ctr, ++ self->aq_hw->curr_stats.dma_pkt_rc, ++ self->aq_hw->curr_stats.dma_oct_rc, ++ &sample); ++ ++ if (self->aq_nic_cfg.is_rx_adaptive) ++ net_dim(&self->rx_dim, &sample); ++ ++ if (self->aq_nic_cfg.is_tx_adaptive) { ++ struct dim_sample tx_sample = {}; ++ ++ dim_update_sample(self->dim_event_ctr, ++ self->aq_hw->curr_stats.dma_pkt_tc, ++ self->aq_hw->curr_stats.dma_oct_tc, ++ &tx_sample); ++ net_dim(&self->tx_dim, &tx_sample); ++ } ++} ++ + /* Recalculate the number of vectors */ + static void aq_nic_cfg_update_num_vecs(struct aq_nic_s *self) + { +@@ -250,6 +331,11 @@ + mutex_unlock(&self->fwreq_mutex); + + aq_nic_update_ndev_stats(self); ++ ++ /* Update DIM if adaptive coalescing is enabled */ ++ if (self->aq_nic_cfg.is_rx_adaptive || ++ self->aq_nic_cfg.is_tx_adaptive) ++ aq_nic_dim_update(self); + } + + static void aq_nic_service_timer_cb(struct timer_list *t) +@@ -424,6 +510,8 @@ + if (err < 0) + goto err_exit; + ++ aq_nic_dim_init(self); ++ + if (ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ATLANTIC) && + self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) { + self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX; +@@ -1432,6 +1520,12 @@ + if (!self) + goto err_exit; + ++ /* Cancel any pending DIM work before teardown */ ++ cancel_work_sync(&self->rx_dim.work); ++ cancel_work_sync(&self->tx_dim.work); ++ self->aq_nic_cfg.is_rx_adaptive = false; ++ self->aq_nic_cfg.is_tx_adaptive = false; ++ + for (i = 0U; i < self->aq_vecs; i++) { + aq_vec = self->aq_vec[i]; + aq_vec_deinit(aq_vec); +--- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c 2026-02-23 01:33:58.379663822 +0000 +@@ -562,6 +562,9 @@ + + cfg = aq_nic_get_cfg(aq_nic); + ++ coal->use_adaptive_rx_coalesce = cfg->is_rx_adaptive; ++ coal->use_adaptive_tx_coalesce = cfg->is_tx_adaptive; ++ + if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON || + cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) { + coal->rx_coalesce_usecs = cfg->rx_itr; +@@ -588,6 +591,23 @@ + + cfg = aq_nic_get_cfg(aq_nic); + ++ /* Handle adaptive coalescing (DIM) mode */ ++ cfg->is_rx_adaptive = !!coal->use_adaptive_rx_coalesce; ++ cfg->is_tx_adaptive = !!coal->use_adaptive_tx_coalesce; ++ ++ if (cfg->is_rx_adaptive || cfg->is_tx_adaptive) { ++ /* Enable HW moderation engine; DIM manages the timers */ ++ cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ ++ /* Allow user to set initial timer values alongside adaptive */ ++ if (coal->rx_coalesce_usecs) ++ cfg->rx_itr = coal->rx_coalesce_usecs; ++ if (coal->tx_coalesce_usecs) ++ cfg->tx_itr = coal->tx_coalesce_usecs; ++ ++ return aq_nic_update_interrupt_moderation_settings(aq_nic); ++ } ++ + /* Atlantic only supports timing based coalescing + */ + if (coal->rx_max_coalesced_frames > 1 || +@@ -1050,7 +1070,8 @@ + + const struct ethtool_ops aq_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | +- ETHTOOL_COALESCE_MAX_FRAMES, ++ ETHTOOL_COALESCE_MAX_FRAMES | ++ ETHTOOL_COALESCE_USE_ADAPTIVE, + .get_link = aq_ethtool_get_link, + .get_regs_len = aq_ethtool_get_regs_len, + .get_regs = aq_ethtool_get_regs, diff --git a/7.0/0008-atlantic-dim.patch b/7.0/0008-atlantic-dim.patch new file mode 100644 index 00000000..e8f4eb91 --- /dev/null +++ b/7.0/0008-atlantic-dim.patch @@ -0,0 +1,225 @@ +--- a/drivers/net/ethernet/aquantia/Kconfig 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/Kconfig 2026-02-23 01:33:03.716143177 +0000 +@@ -20,6 +20,7 @@ + tristate "aQuantia AQtion(tm) Support" + depends on PCI + depends on MACSEC || MACSEC=n ++ select NET_DIM + help + This enables the support for the aQuantia AQtion(tm) Ethernet card. + +--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h 2026-02-23 01:33:13.450057021 +0000 +@@ -12,6 +12,7 @@ + + #include + #include ++#include + #include + + #include "aq_common.h" +@@ -49,6 +50,8 @@ + u32 itr; + u16 rx_itr; + u16 tx_itr; ++ bool is_rx_adaptive; ++ bool is_tx_adaptive; + u32 rxpageorder; + u32 num_rss_queues; + u32 mtu; +@@ -128,6 +131,10 @@ + atomic_t flags; + u32 msg_enable; + struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX]; ++ /* DIM (Dynamic Interrupt Moderation) state */ ++ struct dim rx_dim; ++ struct dim tx_dim; ++ u16 dim_event_ctr; + struct aq_ring_s *aq_ring_tx[AQ_HW_QUEUES_MAX]; + struct aq_hw_s *aq_hw; + struct bpf_prog *xdp_prog; +@@ -218,4 +225,7 @@ + const u32 max_rate); + int aq_nic_setup_tc_min_rate(struct aq_nic_s *self, const unsigned int tc, + const u32 min_rate); ++ ++void aq_nic_dim_update(struct aq_nic_s *self); ++ + #endif /* AQ_NIC_H */ +--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-23 01:33:44.071788263 +0000 +@@ -18,6 +18,7 @@ + #include "aq_ptp.h" + #include "aq_filters.h" + ++#include + #include + #include + #include +@@ -65,6 +66,86 @@ + rss_params->indirection_table[i] = i & (num_rss_queues - 1); + } + ++/* ---- DIM (Dynamic Interrupt Moderation) ---- */ ++ ++static void aq_nic_rx_dim_work(struct work_struct *work) ++{ ++ struct dim *dim = container_of(work, struct dim, work); ++ struct aq_nic_s *nic = container_of(dim, struct aq_nic_s, rx_dim); ++ struct dim_cq_moder moder; ++ ++ moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); ++ ++ /* Clamp to hardware max (0x1FF * 2 = 1022 usec) */ ++ if (moder.usec > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) ++ moder.usec = AQ_CFG_INTERRUPT_MODERATION_USEC_MAX; ++ ++ nic->aq_nic_cfg.rx_itr = moder.usec; ++ nic->aq_nic_cfg.itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ ++ aq_nic_update_interrupt_moderation_settings(nic); ++ ++ dim->state = DIM_START_MEASURE; ++} ++ ++static void aq_nic_tx_dim_work(struct work_struct *work) ++{ ++ struct dim *dim = container_of(work, struct dim, work); ++ struct aq_nic_s *nic = container_of(dim, struct aq_nic_s, tx_dim); ++ struct dim_cq_moder moder; ++ ++ moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix); ++ ++ if (moder.usec > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) ++ moder.usec = AQ_CFG_INTERRUPT_MODERATION_USEC_MAX; ++ ++ nic->aq_nic_cfg.tx_itr = moder.usec; ++ nic->aq_nic_cfg.itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ ++ aq_nic_update_interrupt_moderation_settings(nic); ++ ++ dim->state = DIM_START_MEASURE; ++} ++ ++static void aq_nic_dim_init(struct aq_nic_s *self) ++{ ++ INIT_WORK(&self->rx_dim.work, aq_nic_rx_dim_work); ++ self->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; ++ self->rx_dim.profile_ix = 1; /* start at moderate profile */ ++ ++ INIT_WORK(&self->tx_dim.work, aq_nic_tx_dim_work); ++ self->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; ++ self->tx_dim.profile_ix = 1; ++ ++ self->dim_event_ctr = 0; ++} ++ ++void aq_nic_dim_update(struct aq_nic_s *self) ++{ ++ struct dim_sample sample = {}; ++ ++ self->dim_event_ctr++; ++ ++ /* Use hardware-level DMA packet/byte counters for global view */ ++ dim_update_sample(self->dim_event_ctr, ++ self->aq_hw->curr_stats.dma_pkt_rc, ++ self->aq_hw->curr_stats.dma_oct_rc, ++ &sample); ++ ++ if (self->aq_nic_cfg.is_rx_adaptive) ++ net_dim(&self->rx_dim, &sample); ++ ++ if (self->aq_nic_cfg.is_tx_adaptive) { ++ struct dim_sample tx_sample = {}; ++ ++ dim_update_sample(self->dim_event_ctr, ++ self->aq_hw->curr_stats.dma_pkt_tc, ++ self->aq_hw->curr_stats.dma_oct_tc, ++ &tx_sample); ++ net_dim(&self->tx_dim, &tx_sample); ++ } ++} ++ + /* Recalculate the number of vectors */ + static void aq_nic_cfg_update_num_vecs(struct aq_nic_s *self) + { +@@ -250,6 +331,11 @@ + mutex_unlock(&self->fwreq_mutex); + + aq_nic_update_ndev_stats(self); ++ ++ /* Update DIM if adaptive coalescing is enabled */ ++ if (self->aq_nic_cfg.is_rx_adaptive || ++ self->aq_nic_cfg.is_tx_adaptive) ++ aq_nic_dim_update(self); + } + + static void aq_nic_service_timer_cb(struct timer_list *t) +@@ -424,6 +510,8 @@ + if (err < 0) + goto err_exit; + ++ aq_nic_dim_init(self); ++ + if (ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ATLANTIC) && + self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) { + self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX; +@@ -1432,6 +1520,12 @@ + if (!self) + goto err_exit; + ++ /* Cancel any pending DIM work before teardown */ ++ cancel_work_sync(&self->rx_dim.work); ++ cancel_work_sync(&self->tx_dim.work); ++ self->aq_nic_cfg.is_rx_adaptive = false; ++ self->aq_nic_cfg.is_tx_adaptive = false; ++ + for (i = 0U; i < self->aq_vecs; i++) { + aq_vec = self->aq_vec[i]; + aq_vec_deinit(aq_vec); +--- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c 2026-02-16 16:11:45.000000000 +0000 ++++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c 2026-02-23 01:33:58.379663822 +0000 +@@ -562,6 +562,9 @@ + + cfg = aq_nic_get_cfg(aq_nic); + ++ coal->use_adaptive_rx_coalesce = cfg->is_rx_adaptive; ++ coal->use_adaptive_tx_coalesce = cfg->is_tx_adaptive; ++ + if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON || + cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) { + coal->rx_coalesce_usecs = cfg->rx_itr; +@@ -588,6 +591,23 @@ + + cfg = aq_nic_get_cfg(aq_nic); + ++ /* Handle adaptive coalescing (DIM) mode */ ++ cfg->is_rx_adaptive = !!coal->use_adaptive_rx_coalesce; ++ cfg->is_tx_adaptive = !!coal->use_adaptive_tx_coalesce; ++ ++ if (cfg->is_rx_adaptive || cfg->is_tx_adaptive) { ++ /* Enable HW moderation engine; DIM manages the timers */ ++ cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ ++ /* Allow user to set initial timer values alongside adaptive */ ++ if (coal->rx_coalesce_usecs) ++ cfg->rx_itr = coal->rx_coalesce_usecs; ++ if (coal->tx_coalesce_usecs) ++ cfg->tx_itr = coal->tx_coalesce_usecs; ++ ++ return aq_nic_update_interrupt_moderation_settings(aq_nic); ++ } ++ + /* Atlantic only supports timing based coalescing + */ + if (coal->rx_max_coalesced_frames > 1 || +@@ -1050,7 +1070,8 @@ + + const struct ethtool_ops aq_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | +- ETHTOOL_COALESCE_MAX_FRAMES, ++ ETHTOOL_COALESCE_MAX_FRAMES | ++ ETHTOOL_COALESCE_USE_ADAPTIVE, + .get_link = aq_ethtool_get_link, + .get_regs_len = aq_ethtool_get_regs_len, + .get_regs = aq_ethtool_get_regs, From 9e1777dc429a886b3ca5d5b2d62508aa56a1430e Mon Sep 17 00:00:00 2001 From: 8hodgsonkh Date: Tue, 24 Feb 2026 19:55:45 +0000 Subject: [PATCH 2/2] atlantic: address review feedback on DIM patch --- 6.19/0008-atlantic-dim.patch | 81 ++++++++++++++++++++---------------- 7.0/0008-atlantic-dim.patch | 81 ++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 74 deletions(-) diff --git a/6.19/0008-atlantic-dim.patch b/6.19/0008-atlantic-dim.patch index e8f4eb91..4d203bd5 100644 --- a/6.19/0008-atlantic-dim.patch +++ b/6.19/0008-atlantic-dim.patch @@ -27,25 +27,19 @@ u32 rxpageorder; u32 num_rss_queues; u32 mtu; -@@ -128,6 +131,10 @@ +@@ -128,6 +131,11 @@ atomic_t flags; u32 msg_enable; struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX]; + /* DIM (Dynamic Interrupt Moderation) state */ + struct dim rx_dim; + struct dim tx_dim; -+ u16 dim_event_ctr; ++ u64 dim_rx_event_ctr; ++ u64 dim_tx_event_ctr; struct aq_ring_s *aq_ring_tx[AQ_HW_QUEUES_MAX]; struct aq_hw_s *aq_hw; struct bpf_prog *xdp_prog; -@@ -218,4 +225,7 @@ - const u32 max_rate); - int aq_nic_setup_tc_min_rate(struct aq_nic_s *self, const unsigned int tc, - const u32 min_rate); -+ -+void aq_nic_dim_update(struct aq_nic_s *self); -+ - #endif /* AQ_NIC_H */ +#endif /* AQ_NIC_H */ --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-16 16:11:45.000000000 +0000 +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-23 01:33:44.071788263 +0000 @@ -18,6 +18,7 @@ @@ -111,28 +105,27 @@ + self->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; + self->tx_dim.profile_ix = 1; + -+ self->dim_event_ctr = 0; ++ self->dim_rx_event_ctr = 0; ++ self->dim_tx_event_ctr = 0; +} + -+void aq_nic_dim_update(struct aq_nic_s *self) ++static void aq_nic_dim_update(struct aq_nic_s *self) +{ -+ struct dim_sample sample = {}; -+ -+ self->dim_event_ctr++; -+ + /* Use hardware-level DMA packet/byte counters for global view */ -+ dim_update_sample(self->dim_event_ctr, -+ self->aq_hw->curr_stats.dma_pkt_rc, -+ self->aq_hw->curr_stats.dma_oct_rc, -+ &sample); -+ -+ if (self->aq_nic_cfg.is_rx_adaptive) -+ net_dim(&self->rx_dim, &sample); ++ if (self->aq_nic_cfg.is_rx_adaptive) { ++ struct dim_sample rx_sample = {}; ++ ++ dim_update_sample(++self->dim_rx_event_ctr, ++ self->aq_hw->curr_stats.dma_pkt_rc, ++ self->aq_hw->curr_stats.dma_oct_rc, ++ &rx_sample); ++ net_dim(&self->rx_dim, &rx_sample); ++ } + + if (self->aq_nic_cfg.is_tx_adaptive) { + struct dim_sample tx_sample = {}; + -+ dim_update_sample(self->dim_event_ctr, ++ dim_update_sample(++self->dim_tx_event_ctr, + self->aq_hw->curr_stats.dma_pkt_tc, + self->aq_hw->curr_stats.dma_oct_tc, + &tx_sample); @@ -164,15 +157,19 @@ if (ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ATLANTIC) && self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) { self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX; -@@ -1432,6 +1520,12 @@ +@@ -1432,6 +1520,14 @@ if (!self) goto err_exit; -+ /* Cancel any pending DIM work before teardown */ -+ cancel_work_sync(&self->rx_dim.work); -+ cancel_work_sync(&self->tx_dim.work); ++ /* Disable adaptive flags first to prevent DIM from rescheduling */ + self->aq_nic_cfg.is_rx_adaptive = false; + self->aq_nic_cfg.is_tx_adaptive = false; ++ ++ /* Cancel any pending DIM work before teardown */ ++ if (self->rx_dim.work.func) ++ cancel_work_sync(&self->rx_dim.work); ++ if (self->tx_dim.work.func) ++ cancel_work_sync(&self->tx_dim.work); + for (i = 0U; i < self->aq_vecs; i++) { aq_vec = self->aq_vec[i]; @@ -189,19 +186,28 @@ if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON || cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) { coal->rx_coalesce_usecs = cfg->rx_itr; -@@ -588,6 +591,23 @@ +@@ -588,9 +591,28 @@ cfg = aq_nic_get_cfg(aq_nic); -+ /* Handle adaptive coalescing (DIM) mode */ ++ /* Capture adaptive coalescing (DIM) flags */ + cfg->is_rx_adaptive = !!coal->use_adaptive_rx_coalesce; + cfg->is_tx_adaptive = !!coal->use_adaptive_tx_coalesce; + + /* Atlantic only supports timing based coalescing + */ + if (coal->rx_max_coalesced_frames > 1 || ++ coal->tx_max_coalesced_frames > 1) ++ return -EOPNOTSUPP; ++ ++ /* In adaptive mode, DIM manages the timers automatically */ + if (cfg->is_rx_adaptive || cfg->is_tx_adaptive) { -+ /* Enable HW moderation engine; DIM manages the timers */ -+ cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ /* Clamp any user-provided initial timers to hardware max */ ++ if (coal->rx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX || ++ coal->tx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) ++ return -EINVAL; + -+ /* Allow user to set initial timer values alongside adaptive */ ++ cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; + if (coal->rx_coalesce_usecs) + cfg->rx_itr = coal->rx_coalesce_usecs; + if (coal->tx_coalesce_usecs) @@ -210,10 +216,11 @@ + return aq_nic_update_interrupt_moderation_settings(aq_nic); + } + - /* Atlantic only supports timing based coalescing - */ - if (coal->rx_max_coalesced_frames > 1 || -@@ -1050,7 +1070,8 @@ ++ /* We do not support frame counting. Check this ++ */ ++ if (!(coal->rx_max_coalesced_frames == !coal->rx_coalesce_usecs)) ++ return -EOPNOTSUPP; ++@@ -1050,7 +1072,8 @@ const struct ethtool_ops aq_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS | diff --git a/7.0/0008-atlantic-dim.patch b/7.0/0008-atlantic-dim.patch index e8f4eb91..4d203bd5 100644 --- a/7.0/0008-atlantic-dim.patch +++ b/7.0/0008-atlantic-dim.patch @@ -27,25 +27,19 @@ u32 rxpageorder; u32 num_rss_queues; u32 mtu; -@@ -128,6 +131,10 @@ +@@ -128,6 +131,11 @@ atomic_t flags; u32 msg_enable; struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX]; + /* DIM (Dynamic Interrupt Moderation) state */ + struct dim rx_dim; + struct dim tx_dim; -+ u16 dim_event_ctr; ++ u64 dim_rx_event_ctr; ++ u64 dim_tx_event_ctr; struct aq_ring_s *aq_ring_tx[AQ_HW_QUEUES_MAX]; struct aq_hw_s *aq_hw; struct bpf_prog *xdp_prog; -@@ -218,4 +225,7 @@ - const u32 max_rate); - int aq_nic_setup_tc_min_rate(struct aq_nic_s *self, const unsigned int tc, - const u32 min_rate); -+ -+void aq_nic_dim_update(struct aq_nic_s *self); -+ - #endif /* AQ_NIC_H */ +#endif /* AQ_NIC_H */ --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-16 16:11:45.000000000 +0000 +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 2026-02-23 01:33:44.071788263 +0000 @@ -18,6 +18,7 @@ @@ -111,28 +105,27 @@ + self->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; + self->tx_dim.profile_ix = 1; + -+ self->dim_event_ctr = 0; ++ self->dim_rx_event_ctr = 0; ++ self->dim_tx_event_ctr = 0; +} + -+void aq_nic_dim_update(struct aq_nic_s *self) ++static void aq_nic_dim_update(struct aq_nic_s *self) +{ -+ struct dim_sample sample = {}; -+ -+ self->dim_event_ctr++; -+ + /* Use hardware-level DMA packet/byte counters for global view */ -+ dim_update_sample(self->dim_event_ctr, -+ self->aq_hw->curr_stats.dma_pkt_rc, -+ self->aq_hw->curr_stats.dma_oct_rc, -+ &sample); -+ -+ if (self->aq_nic_cfg.is_rx_adaptive) -+ net_dim(&self->rx_dim, &sample); ++ if (self->aq_nic_cfg.is_rx_adaptive) { ++ struct dim_sample rx_sample = {}; ++ ++ dim_update_sample(++self->dim_rx_event_ctr, ++ self->aq_hw->curr_stats.dma_pkt_rc, ++ self->aq_hw->curr_stats.dma_oct_rc, ++ &rx_sample); ++ net_dim(&self->rx_dim, &rx_sample); ++ } + + if (self->aq_nic_cfg.is_tx_adaptive) { + struct dim_sample tx_sample = {}; + -+ dim_update_sample(self->dim_event_ctr, ++ dim_update_sample(++self->dim_tx_event_ctr, + self->aq_hw->curr_stats.dma_pkt_tc, + self->aq_hw->curr_stats.dma_oct_tc, + &tx_sample); @@ -164,15 +157,19 @@ if (ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ATLANTIC) && self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) { self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX; -@@ -1432,6 +1520,12 @@ +@@ -1432,6 +1520,14 @@ if (!self) goto err_exit; -+ /* Cancel any pending DIM work before teardown */ -+ cancel_work_sync(&self->rx_dim.work); -+ cancel_work_sync(&self->tx_dim.work); ++ /* Disable adaptive flags first to prevent DIM from rescheduling */ + self->aq_nic_cfg.is_rx_adaptive = false; + self->aq_nic_cfg.is_tx_adaptive = false; ++ ++ /* Cancel any pending DIM work before teardown */ ++ if (self->rx_dim.work.func) ++ cancel_work_sync(&self->rx_dim.work); ++ if (self->tx_dim.work.func) ++ cancel_work_sync(&self->tx_dim.work); + for (i = 0U; i < self->aq_vecs; i++) { aq_vec = self->aq_vec[i]; @@ -189,19 +186,28 @@ if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON || cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) { coal->rx_coalesce_usecs = cfg->rx_itr; -@@ -588,6 +591,23 @@ +@@ -588,9 +591,28 @@ cfg = aq_nic_get_cfg(aq_nic); -+ /* Handle adaptive coalescing (DIM) mode */ ++ /* Capture adaptive coalescing (DIM) flags */ + cfg->is_rx_adaptive = !!coal->use_adaptive_rx_coalesce; + cfg->is_tx_adaptive = !!coal->use_adaptive_tx_coalesce; + + /* Atlantic only supports timing based coalescing + */ + if (coal->rx_max_coalesced_frames > 1 || ++ coal->tx_max_coalesced_frames > 1) ++ return -EOPNOTSUPP; ++ ++ /* In adaptive mode, DIM manages the timers automatically */ + if (cfg->is_rx_adaptive || cfg->is_tx_adaptive) { -+ /* Enable HW moderation engine; DIM manages the timers */ -+ cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; ++ /* Clamp any user-provided initial timers to hardware max */ ++ if (coal->rx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX || ++ coal->tx_coalesce_usecs > AQ_CFG_INTERRUPT_MODERATION_USEC_MAX) ++ return -EINVAL; + -+ /* Allow user to set initial timer values alongside adaptive */ ++ cfg->itr = AQ_CFG_INTERRUPT_MODERATION_ON; + if (coal->rx_coalesce_usecs) + cfg->rx_itr = coal->rx_coalesce_usecs; + if (coal->tx_coalesce_usecs) @@ -210,10 +216,11 @@ + return aq_nic_update_interrupt_moderation_settings(aq_nic); + } + - /* Atlantic only supports timing based coalescing - */ - if (coal->rx_max_coalesced_frames > 1 || -@@ -1050,7 +1070,8 @@ ++ /* We do not support frame counting. Check this ++ */ ++ if (!(coal->rx_max_coalesced_frames == !coal->rx_coalesce_usecs)) ++ return -EOPNOTSUPP; ++@@ -1050,7 +1072,8 @@ const struct ethtool_ops aq_ethtool_ops = { .supported_coalesce_params = ETHTOOL_COALESCE_USECS |