Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions 6.19/0008-atlantic-dim.patch
Original file line number Diff line number Diff line change
@@ -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 <linux/ethtool.h>
#include <net/xdp.h>
+#include <linux/dim.h>
#include <linux/bpf.h>

#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;

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dim_event_ctr is declared as u16, but it is used as the monotonically increasing event_ctr input to dim_update_sample(). A 16-bit counter will wrap quickly and can confuse DIM’s rate calculations. Consider using a wider type (e.g., u32/u64) for the event counter (and keep it monotonically increasing for the lifetime of the DIM instance).

Suggested change
+ u16 dim_event_ctr;
+ u32 dim_event_ctr;

Copilot uses AI. Check for mistakes.
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);

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aq_nic_dim_update() is only referenced within aq_nic.c in this patch, but it’s declared in aq_nic.h and defined non-static. If no other translation unit needs it, keep it static and drop the header prototype to avoid unnecessarily expanding the driver’s internal API surface.

Suggested change
+void aq_nic_dim_update(struct aq_nic_s *self);

Copilot uses AI. Check for mistakes.
+
#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 <linux/dim.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -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);
+ }

Copilot AI Feb 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the adaptive-coalesce branch, the early return aq_nic_update_interrupt_moderation_settings() bypasses the existing validation below (e.g., checks rejecting rx_max_coalesced_frames > 1, and any bounds checks for usecs). This can allow invalid/unsupported coalesce parameters to be accepted whenever adaptive mode is enabled. Keep the same parameter validation in place before returning (or replicate the relevant checks inside the adaptive branch).

Copilot uses AI. Check for mistakes.
+
/* 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,
Loading
Loading