Commit 6fb70fd1 authored by Ben Hutchings's avatar Ben Hutchings Committed by David S. Miller

sfc: Implement adaptive IRQ moderation

Calculate a score for each 1000 IRQs:
- TX completions are worth 1 point
- RX completions are worth 4 if merged using LRO or 2 otherwise

Reduce moderation if the score is less than 10000, down to a minimum
of 5 us.  Increase moderation if the score is more than 20000, up to
the specified maximum.
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 85451a95
...@@ -133,6 +133,16 @@ static int phy_flash_cfg; ...@@ -133,6 +133,16 @@ static int phy_flash_cfg;
module_param(phy_flash_cfg, int, 0644); module_param(phy_flash_cfg, int, 0644);
MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially");
static unsigned irq_adapt_low_thresh = 10000;
module_param(irq_adapt_low_thresh, uint, 0644);
MODULE_PARM_DESC(irq_adapt_low_thresh,
"Threshold score for reducing IRQ moderation");
static unsigned irq_adapt_high_thresh = 20000;
module_param(irq_adapt_high_thresh, uint, 0644);
MODULE_PARM_DESC(irq_adapt_high_thresh,
"Threshold score for increasing IRQ moderation");
/************************************************************************** /**************************************************************************
* *
* Utility functions and prototypes * Utility functions and prototypes
...@@ -223,6 +233,35 @@ static int efx_poll(struct napi_struct *napi, int budget) ...@@ -223,6 +233,35 @@ static int efx_poll(struct napi_struct *napi, int budget)
rx_packets = efx_process_channel(channel, budget); rx_packets = efx_process_channel(channel, budget);
if (rx_packets < budget) { if (rx_packets < budget) {
struct efx_nic *efx = channel->efx;
if (channel->used_flags & EFX_USED_BY_RX &&
efx->irq_rx_adaptive &&
unlikely(++channel->irq_count == 1000)) {
unsigned old_irq_moderation = channel->irq_moderation;
if (unlikely(channel->irq_mod_score <
irq_adapt_low_thresh)) {
channel->irq_moderation =
max_t(int,
channel->irq_moderation -
FALCON_IRQ_MOD_RESOLUTION,
FALCON_IRQ_MOD_RESOLUTION);
} else if (unlikely(channel->irq_mod_score >
irq_adapt_high_thresh)) {
channel->irq_moderation =
min(channel->irq_moderation +
FALCON_IRQ_MOD_RESOLUTION,
efx->irq_rx_moderation);
}
if (channel->irq_moderation != old_irq_moderation)
falcon_set_int_moderation(channel);
channel->irq_count = 0;
channel->irq_mod_score = 0;
}
/* There is no race here; although napi_disable() will /* There is no race here; although napi_disable() will
* only wait for napi_complete(), this isn't a problem * only wait for napi_complete(), this isn't a problem
* since efx_channel_processed() will have no effect if * since efx_channel_processed() will have no effect if
...@@ -991,7 +1030,7 @@ static int efx_probe_nic(struct efx_nic *efx) ...@@ -991,7 +1030,7 @@ static int efx_probe_nic(struct efx_nic *efx)
efx_set_channels(efx); efx_set_channels(efx);
/* Initialise the interrupt moderation settings */ /* Initialise the interrupt moderation settings */
efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec); efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true);
return 0; return 0;
} }
...@@ -1188,7 +1227,8 @@ void efx_flush_queues(struct efx_nic *efx) ...@@ -1188,7 +1227,8 @@ void efx_flush_queues(struct efx_nic *efx)
**************************************************************************/ **************************************************************************/
/* Set interrupt moderation parameters */ /* Set interrupt moderation parameters */
void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs,
bool rx_adaptive)
{ {
struct efx_tx_queue *tx_queue; struct efx_tx_queue *tx_queue;
struct efx_rx_queue *rx_queue; struct efx_rx_queue *rx_queue;
...@@ -1198,6 +1238,8 @@ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs) ...@@ -1198,6 +1238,8 @@ void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, int rx_usecs)
efx_for_each_tx_queue(tx_queue, efx) efx_for_each_tx_queue(tx_queue, efx)
tx_queue->channel->irq_moderation = tx_usecs; tx_queue->channel->irq_moderation = tx_usecs;
efx->irq_rx_adaptive = rx_adaptive;
efx->irq_rx_moderation = rx_usecs;
efx_for_each_rx_queue(rx_queue, efx) efx_for_each_rx_queue(rx_queue, efx)
rx_queue->channel->irq_moderation = rx_usecs; rx_queue->channel->irq_moderation = rx_usecs;
} }
......
...@@ -52,7 +52,7 @@ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); ...@@ -52,7 +52,7 @@ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
extern void efx_suspend(struct efx_nic *efx); extern void efx_suspend(struct efx_nic *efx);
extern void efx_resume(struct efx_nic *efx); extern void efx_resume(struct efx_nic *efx);
extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs, extern void efx_init_irq_moderation(struct efx_nic *efx, int tx_usecs,
int rx_usecs); int rx_usecs, bool rx_adaptive);
extern int efx_request_power(struct efx_nic *efx, int mw, const char *name); extern int efx_request_power(struct efx_nic *efx, int mw, const char *name);
extern void efx_hex_dump(const u8 *, unsigned int, const char *); extern void efx_hex_dump(const u8 *, unsigned int, const char *);
......
...@@ -604,7 +604,6 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev, ...@@ -604,7 +604,6 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev,
{ {
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
struct efx_tx_queue *tx_queue; struct efx_tx_queue *tx_queue;
struct efx_rx_queue *rx_queue;
struct efx_channel *channel; struct efx_channel *channel;
memset(coalesce, 0, sizeof(*coalesce)); memset(coalesce, 0, sizeof(*coalesce));
...@@ -622,14 +621,8 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev, ...@@ -622,14 +621,8 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev,
} }
} }
/* Find lowest IRQ moderation across all used RX queues */ coalesce->use_adaptive_rx_coalesce = efx->irq_rx_adaptive;
coalesce->rx_coalesce_usecs_irq = ~((u32) 0); coalesce->rx_coalesce_usecs_irq = efx->irq_rx_moderation;
efx_for_each_rx_queue(rx_queue, efx) {
channel = rx_queue->channel;
if (channel->irq_moderation < coalesce->rx_coalesce_usecs_irq)
coalesce->rx_coalesce_usecs_irq =
channel->irq_moderation;
}
return 0; return 0;
} }
...@@ -643,10 +636,9 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, ...@@ -643,10 +636,9 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
struct efx_nic *efx = netdev_priv(net_dev); struct efx_nic *efx = netdev_priv(net_dev);
struct efx_channel *channel; struct efx_channel *channel;
struct efx_tx_queue *tx_queue; struct efx_tx_queue *tx_queue;
unsigned tx_usecs, rx_usecs; unsigned tx_usecs, rx_usecs, adaptive;
if (coalesce->use_adaptive_rx_coalesce || if (coalesce->use_adaptive_tx_coalesce)
coalesce->use_adaptive_tx_coalesce)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) { if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) {
...@@ -657,6 +649,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, ...@@ -657,6 +649,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
rx_usecs = coalesce->rx_coalesce_usecs_irq; rx_usecs = coalesce->rx_coalesce_usecs_irq;
tx_usecs = coalesce->tx_coalesce_usecs_irq; tx_usecs = coalesce->tx_coalesce_usecs_irq;
adaptive = coalesce->use_adaptive_rx_coalesce;
/* If the channel is shared only allow RX parameters to be set */ /* If the channel is shared only allow RX parameters to be set */
efx_for_each_tx_queue(tx_queue, efx) { efx_for_each_tx_queue(tx_queue, efx) {
...@@ -668,7 +661,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev, ...@@ -668,7 +661,7 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
} }
} }
efx_init_irq_moderation(efx, tx_usecs, rx_usecs); efx_init_irq_moderation(efx, tx_usecs, rx_usecs, adaptive);
/* Reset channel to pick up new moderation value. Note that /* Reset channel to pick up new moderation value. Note that
* this may change the value of the irq_moderation field * this may change the value of the irq_moderation field
......
...@@ -729,6 +729,9 @@ static void falcon_handle_tx_event(struct efx_channel *channel, ...@@ -729,6 +729,9 @@ static void falcon_handle_tx_event(struct efx_channel *channel,
tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR); tx_ev_desc_ptr = EFX_QWORD_FIELD(*event, TX_EV_DESC_PTR);
tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL); tx_ev_q_label = EFX_QWORD_FIELD(*event, TX_EV_Q_LABEL);
tx_queue = &efx->tx_queue[tx_ev_q_label]; tx_queue = &efx->tx_queue[tx_ev_q_label];
channel->irq_mod_score +=
(tx_ev_desc_ptr - tx_queue->read_count) &
efx->type->txd_ring_mask;
efx_xmit_done(tx_queue, tx_ev_desc_ptr); efx_xmit_done(tx_queue, tx_ev_desc_ptr);
} else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) { } else if (EFX_QWORD_FIELD(*event, TX_EV_WQ_FF_FULL)) {
/* Rewrite the FIFO write pointer */ /* Rewrite the FIFO write pointer */
...@@ -898,6 +901,8 @@ static void falcon_handle_rx_event(struct efx_channel *channel, ...@@ -898,6 +901,8 @@ static void falcon_handle_rx_event(struct efx_channel *channel,
discard = true; discard = true;
} }
channel->irq_mod_score += 2;
/* Handle received packet */ /* Handle received packet */
efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt, efx_rx_packet(rx_queue, rx_ev_desc_ptr, rx_ev_byte_cnt,
checksummed, discard); checksummed, discard);
...@@ -1075,14 +1080,15 @@ void falcon_set_int_moderation(struct efx_channel *channel) ...@@ -1075,14 +1080,15 @@ void falcon_set_int_moderation(struct efx_channel *channel)
* program is based at 0. So actual interrupt moderation * program is based at 0. So actual interrupt moderation
* achieved is ((x + 1) * res). * achieved is ((x + 1) * res).
*/ */
unsigned int res = 5; channel->irq_moderation -= (channel->irq_moderation %
channel->irq_moderation -= (channel->irq_moderation % res); FALCON_IRQ_MOD_RESOLUTION);
if (channel->irq_moderation < res) if (channel->irq_moderation < FALCON_IRQ_MOD_RESOLUTION)
channel->irq_moderation = res; channel->irq_moderation = FALCON_IRQ_MOD_RESOLUTION;
EFX_POPULATE_DWORD_2(timer_cmd, EFX_POPULATE_DWORD_2(timer_cmd,
TIMER_MODE, TIMER_MODE_INT_HLDOFF, TIMER_MODE, TIMER_MODE_INT_HLDOFF,
TIMER_VAL, TIMER_VAL,
(channel->irq_moderation / res) - 1); channel->irq_moderation /
FALCON_IRQ_MOD_RESOLUTION - 1);
} else { } else {
EFX_POPULATE_DWORD_2(timer_cmd, EFX_POPULATE_DWORD_2(timer_cmd,
TIMER_MODE, TIMER_MODE_DIS, TIMER_MODE, TIMER_MODE_DIS,
......
...@@ -85,6 +85,8 @@ extern void falcon_set_int_moderation(struct efx_channel *channel); ...@@ -85,6 +85,8 @@ extern void falcon_set_int_moderation(struct efx_channel *channel);
extern void falcon_disable_interrupts(struct efx_nic *efx); extern void falcon_disable_interrupts(struct efx_nic *efx);
extern void falcon_fini_interrupt(struct efx_nic *efx); extern void falcon_fini_interrupt(struct efx_nic *efx);
#define FALCON_IRQ_MOD_RESOLUTION 5
/* Global Resources */ /* Global Resources */
extern int falcon_probe_nic(struct efx_nic *efx); extern int falcon_probe_nic(struct efx_nic *efx);
extern int falcon_probe_resources(struct efx_nic *efx); extern int falcon_probe_resources(struct efx_nic *efx);
......
...@@ -336,6 +336,8 @@ enum efx_rx_alloc_method { ...@@ -336,6 +336,8 @@ enum efx_rx_alloc_method {
* @eventq_read_ptr: Event queue read pointer * @eventq_read_ptr: Event queue read pointer
* @last_eventq_read_ptr: Last event queue read pointer value. * @last_eventq_read_ptr: Last event queue read pointer value.
* @eventq_magic: Event queue magic value for driver-generated test events * @eventq_magic: Event queue magic value for driver-generated test events
* @irq_count: Number of IRQs since last adaptive moderation decision
* @irq_mod_score: IRQ moderation score
* @rx_alloc_level: Watermark based heuristic counter for pushing descriptors * @rx_alloc_level: Watermark based heuristic counter for pushing descriptors
* and diagnostic counters * and diagnostic counters
* @rx_alloc_push_pages: RX allocation method currently in use for pushing * @rx_alloc_push_pages: RX allocation method currently in use for pushing
...@@ -364,6 +366,9 @@ struct efx_channel { ...@@ -364,6 +366,9 @@ struct efx_channel {
unsigned int last_eventq_read_ptr; unsigned int last_eventq_read_ptr;
unsigned int eventq_magic; unsigned int eventq_magic;
unsigned int irq_count;
unsigned int irq_mod_score;
int rx_alloc_level; int rx_alloc_level;
int rx_alloc_push_pages; int rx_alloc_push_pages;
...@@ -703,6 +708,8 @@ union efx_multicast_hash { ...@@ -703,6 +708,8 @@ union efx_multicast_hash {
* @membase: Memory BAR value * @membase: Memory BAR value
* @biu_lock: BIU (bus interface unit) lock * @biu_lock: BIU (bus interface unit) lock
* @interrupt_mode: Interrupt mode * @interrupt_mode: Interrupt mode
* @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues
* @irq_rx_moderation: IRQ moderation time for RX event queues
* @i2c_adap: I2C adapter * @i2c_adap: I2C adapter
* @board_info: Board-level information * @board_info: Board-level information
* @state: Device state flag. Serialised by the rtnl_lock. * @state: Device state flag. Serialised by the rtnl_lock.
...@@ -784,6 +791,8 @@ struct efx_nic { ...@@ -784,6 +791,8 @@ struct efx_nic {
void __iomem *membase; void __iomem *membase;
spinlock_t biu_lock; spinlock_t biu_lock;
enum efx_int_mode interrupt_mode; enum efx_int_mode interrupt_mode;
bool irq_rx_adaptive;
unsigned int irq_rx_moderation;
struct i2c_adapter i2c_adap; struct i2c_adapter i2c_adap;
struct efx_board board_info; struct efx_board board_info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment