Commit 539de7c5 authored by Bert Kenward's avatar Bert Kenward Committed by David S. Miller

sfc: set interrupt moderation via MCDI

SFN8000-series NICs require a new method of setting interrupt moderation,
via MCDI. This is indicated by a workaround flag. This new MCDI command
takes an explicit time value rather than a number of ticks. It therefore
makes sense to also store the moderation values in terms of time, since
that is what the ethtool interface is interested in.
Signed-off-by: default avatarBert Kenward <bkenward@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a995560a
...@@ -1749,27 +1749,43 @@ static size_t efx_ef10_update_stats_vf(struct efx_nic *efx, u64 *full_stats, ...@@ -1749,27 +1749,43 @@ static size_t efx_ef10_update_stats_vf(struct efx_nic *efx, u64 *full_stats,
static void efx_ef10_push_irq_moderation(struct efx_channel *channel) static void efx_ef10_push_irq_moderation(struct efx_channel *channel)
{ {
struct efx_nic *efx = channel->efx; struct efx_nic *efx = channel->efx;
unsigned int mode, value; unsigned int mode, usecs;
efx_dword_t timer_cmd; efx_dword_t timer_cmd;
if (channel->irq_moderation) { if (channel->irq_moderation_us) {
mode = 3; mode = 3;
value = channel->irq_moderation - 1; usecs = channel->irq_moderation_us;
} else { } else {
mode = 0; mode = 0;
value = 0; usecs = 0;
} }
if (EFX_EF10_WORKAROUND_35388(efx)) { if (EFX_EF10_WORKAROUND_61265(efx)) {
MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_EVQ_TMR_IN_LEN);
unsigned int ns = usecs * 1000;
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_INSTANCE,
channel->channel);
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_TMR_LOAD_REQ_NS, ns);
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_TMR_RELOAD_REQ_NS, ns);
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_TMR_MODE, mode);
efx_mcdi_rpc_async(efx, MC_CMD_SET_EVQ_TMR,
inbuf, sizeof(inbuf), 0, NULL, 0);
} else if (EFX_EF10_WORKAROUND_35388(efx)) {
unsigned int ticks = efx_usecs_to_ticks(efx, usecs);
EFX_POPULATE_DWORD_3(timer_cmd, ERF_DD_EVQ_IND_TIMER_FLAGS, EFX_POPULATE_DWORD_3(timer_cmd, ERF_DD_EVQ_IND_TIMER_FLAGS,
EFE_DD_EVQ_IND_TIMER_FLAGS, EFE_DD_EVQ_IND_TIMER_FLAGS,
ERF_DD_EVQ_IND_TIMER_MODE, mode, ERF_DD_EVQ_IND_TIMER_MODE, mode,
ERF_DD_EVQ_IND_TIMER_VAL, value); ERF_DD_EVQ_IND_TIMER_VAL, ticks);
efx_writed_page(efx, &timer_cmd, ER_DD_EVQ_INDIRECT, efx_writed_page(efx, &timer_cmd, ER_DD_EVQ_INDIRECT,
channel->channel); channel->channel);
} else { } else {
unsigned int ticks = efx_usecs_to_ticks(efx, usecs);
EFX_POPULATE_DWORD_2(timer_cmd, ERF_DZ_TC_TIMER_MODE, mode, EFX_POPULATE_DWORD_2(timer_cmd, ERF_DZ_TC_TIMER_MODE, mode,
ERF_DZ_TC_TIMER_VAL, value); ERF_DZ_TC_TIMER_VAL, ticks);
efx_writed_page(efx, &timer_cmd, ER_DZ_EVQ_TMR, efx_writed_page(efx, &timer_cmd, ER_DZ_EVQ_TMR,
channel->channel); channel->channel);
} }
...@@ -2615,10 +2631,11 @@ static int efx_ef10_ev_init(struct efx_channel *channel) ...@@ -2615,10 +2631,11 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
/* Successfully created event queue on channel 0 */ /* Successfully created event queue on channel 0 */
rc = efx_mcdi_get_workarounds(efx, &implemented, &enabled); rc = efx_mcdi_get_workarounds(efx, &implemented, &enabled);
if (rc == -ENOSYS) { if (rc == -ENOSYS) {
/* GET_WORKAROUNDS was implemented before the bug26807 /* GET_WORKAROUNDS was implemented before these workarounds,
* workaround, thus the latter must be unavailable in this fw * thus they must be unavailable in this firmware.
*/ */
nic_data->workaround_26807 = false; nic_data->workaround_26807 = false;
nic_data->workaround_61265 = false;
rc = 0; rc = 0;
} else if (rc) { } else if (rc) {
goto fail; goto fail;
...@@ -2658,6 +2675,9 @@ static int efx_ef10_ev_init(struct efx_channel *channel) ...@@ -2658,6 +2675,9 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
rc = 0; rc = 0;
} }
} }
nic_data->workaround_61265 =
!!(implemented & MC_CMD_GET_WORKAROUNDS_OUT_BUG61265);
} }
if (!rc) if (!rc)
......
...@@ -281,6 +281,27 @@ static int efx_process_channel(struct efx_channel *channel, int budget) ...@@ -281,6 +281,27 @@ static int efx_process_channel(struct efx_channel *channel, int budget)
* NAPI guarantees serialisation of polls of the same device, which * NAPI guarantees serialisation of polls of the same device, which
* provides the guarantee required by efx_process_channel(). * provides the guarantee required by efx_process_channel().
*/ */
static void efx_update_irq_mod(struct efx_nic *efx, struct efx_channel *channel)
{
int step = efx->irq_mod_step_us;
if (channel->irq_mod_score < irq_adapt_low_thresh) {
if (channel->irq_moderation_us > step) {
channel->irq_moderation_us -= step;
efx->type->push_irq_moderation(channel);
}
} else if (channel->irq_mod_score > irq_adapt_high_thresh) {
if (channel->irq_moderation_us <
efx->irq_rx_moderation_us) {
channel->irq_moderation_us += step;
efx->type->push_irq_moderation(channel);
}
}
channel->irq_count = 0;
channel->irq_mod_score = 0;
}
static int efx_poll(struct napi_struct *napi, int budget) static int efx_poll(struct napi_struct *napi, int budget)
{ {
struct efx_channel *channel = struct efx_channel *channel =
...@@ -301,22 +322,7 @@ static int efx_poll(struct napi_struct *napi, int budget) ...@@ -301,22 +322,7 @@ static int efx_poll(struct napi_struct *napi, int budget)
if (efx_channel_has_rx_queue(channel) && if (efx_channel_has_rx_queue(channel) &&
efx->irq_rx_adaptive && efx->irq_rx_adaptive &&
unlikely(++channel->irq_count == 1000)) { unlikely(++channel->irq_count == 1000)) {
if (unlikely(channel->irq_mod_score < efx_update_irq_mod(efx, channel);
irq_adapt_low_thresh)) {
if (channel->irq_moderation > 1) {
channel->irq_moderation -= 1;
efx->type->push_irq_moderation(channel);
}
} else if (unlikely(channel->irq_mod_score >
irq_adapt_high_thresh)) {
if (channel->irq_moderation <
efx->irq_rx_moderation) {
channel->irq_moderation += 1;
efx->type->push_irq_moderation(channel);
}
}
channel->irq_count = 0;
channel->irq_mod_score = 0;
} }
efx_filter_rfs_expire(channel); efx_filter_rfs_expire(channel);
...@@ -1703,6 +1709,7 @@ static int efx_probe_nic(struct efx_nic *efx) ...@@ -1703,6 +1709,7 @@ static int efx_probe_nic(struct efx_nic *efx)
netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels); netif_set_real_num_rx_queues(efx->net_dev, efx->n_rx_channels);
/* Initialise the interrupt moderation settings */ /* Initialise the interrupt moderation settings */
efx->irq_mod_step_us = DIV_ROUND_UP(efx->timer_quantum_ns, 1000);
efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true, efx_init_irq_moderation(efx, tx_irq_mod_usec, rx_irq_mod_usec, true,
true); true);
...@@ -1949,14 +1956,21 @@ static void efx_remove_all(struct efx_nic *efx) ...@@ -1949,14 +1956,21 @@ static void efx_remove_all(struct efx_nic *efx)
* Interrupt moderation * Interrupt moderation
* *
**************************************************************************/ **************************************************************************/
unsigned int efx_usecs_to_ticks(struct efx_nic *efx, unsigned int usecs)
static unsigned int irq_mod_ticks(unsigned int usecs, unsigned int quantum_ns)
{ {
if (usecs == 0) if (usecs == 0)
return 0; return 0;
if (usecs * 1000 < quantum_ns) if (usecs * 1000 < efx->timer_quantum_ns)
return 1; /* never round down to 0 */ return 1; /* never round down to 0 */
return usecs * 1000 / quantum_ns; return usecs * 1000 / efx->timer_quantum_ns;
}
unsigned int efx_ticks_to_usecs(struct efx_nic *efx, unsigned int ticks)
{
/* We must round up when converting ticks to microseconds
* because we round down when converting the other way.
*/
return DIV_ROUND_UP(ticks * efx->timer_quantum_ns, 1000);
} }
/* Set interrupt moderation parameters */ /* Set interrupt moderation parameters */
...@@ -1968,18 +1982,12 @@ int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, ...@@ -1968,18 +1982,12 @@ int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
unsigned int irq_mod_max = DIV_ROUND_UP(efx->type->timer_period_max * unsigned int irq_mod_max = DIV_ROUND_UP(efx->type->timer_period_max *
efx->timer_quantum_ns, efx->timer_quantum_ns,
1000); 1000);
unsigned int tx_ticks;
unsigned int rx_ticks;
EFX_ASSERT_RESET_SERIALISED(efx); EFX_ASSERT_RESET_SERIALISED(efx);
if (tx_usecs > irq_mod_max || rx_usecs > irq_mod_max) if (tx_usecs > irq_mod_max || rx_usecs > irq_mod_max)
return -EINVAL; return -EINVAL;
tx_ticks = irq_mod_ticks(tx_usecs, efx->timer_quantum_ns); if (tx_usecs != rx_usecs && efx->tx_channel_offset == 0 &&
rx_ticks = irq_mod_ticks(rx_usecs, efx->timer_quantum_ns);
if (tx_ticks != rx_ticks && efx->tx_channel_offset == 0 &&
!rx_may_override_tx) { !rx_may_override_tx) {
netif_err(efx, drv, efx->net_dev, "Channels are shared. " netif_err(efx, drv, efx->net_dev, "Channels are shared. "
"RX and TX IRQ moderation must be equal\n"); "RX and TX IRQ moderation must be equal\n");
...@@ -1987,12 +1995,12 @@ int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, ...@@ -1987,12 +1995,12 @@ int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
} }
efx->irq_rx_adaptive = rx_adaptive; efx->irq_rx_adaptive = rx_adaptive;
efx->irq_rx_moderation = rx_ticks; efx->irq_rx_moderation_us = rx_usecs;
efx_for_each_channel(channel, efx) { efx_for_each_channel(channel, efx) {
if (efx_channel_has_rx_queue(channel)) if (efx_channel_has_rx_queue(channel))
channel->irq_moderation = rx_ticks; channel->irq_moderation_us = rx_usecs;
else if (efx_channel_has_tx_queues(channel)) else if (efx_channel_has_tx_queues(channel))
channel->irq_moderation = tx_ticks; channel->irq_moderation_us = tx_usecs;
} }
return 0; return 0;
...@@ -2001,26 +2009,21 @@ int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, ...@@ -2001,26 +2009,21 @@ int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs, void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs,
unsigned int *rx_usecs, bool *rx_adaptive) unsigned int *rx_usecs, bool *rx_adaptive)
{ {
/* We must round up when converting ticks to microseconds
* because we round down when converting the other way.
*/
*rx_adaptive = efx->irq_rx_adaptive; *rx_adaptive = efx->irq_rx_adaptive;
*rx_usecs = DIV_ROUND_UP(efx->irq_rx_moderation * *rx_usecs = efx->irq_rx_moderation_us;
efx->timer_quantum_ns,
1000);
/* If channels are shared between RX and TX, so is IRQ /* If channels are shared between RX and TX, so is IRQ
* moderation. Otherwise, IRQ moderation is the same for all * moderation. Otherwise, IRQ moderation is the same for all
* TX channels and is not adaptive. * TX channels and is not adaptive.
*/ */
if (efx->tx_channel_offset == 0) if (efx->tx_channel_offset == 0) {
*tx_usecs = *rx_usecs; *tx_usecs = *rx_usecs;
else } else {
*tx_usecs = DIV_ROUND_UP( struct efx_channel *tx_channel;
efx->channel[efx->tx_channel_offset]->irq_moderation *
efx->timer_quantum_ns, tx_channel = efx->channel[efx->tx_channel_offset];
1000); *tx_usecs = tx_channel->irq_moderation_us;
}
} }
/************************************************************************** /**************************************************************************
......
...@@ -204,6 +204,8 @@ int efx_try_recovery(struct efx_nic *efx); ...@@ -204,6 +204,8 @@ int efx_try_recovery(struct efx_nic *efx);
/* Global */ /* Global */
void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); void efx_schedule_reset(struct efx_nic *efx, enum reset_type type);
unsigned int efx_usecs_to_ticks(struct efx_nic *efx, unsigned int usecs);
unsigned int efx_ticks_to_usecs(struct efx_nic *efx, unsigned int ticks);
int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs,
unsigned int rx_usecs, bool rx_adaptive, unsigned int rx_usecs, bool rx_adaptive,
bool rx_may_override_tx); bool rx_may_override_tx);
......
...@@ -378,12 +378,15 @@ static void falcon_push_irq_moderation(struct efx_channel *channel) ...@@ -378,12 +378,15 @@ static void falcon_push_irq_moderation(struct efx_channel *channel)
struct efx_nic *efx = channel->efx; struct efx_nic *efx = channel->efx;
/* Set timer register */ /* Set timer register */
if (channel->irq_moderation) { if (channel->irq_moderation_us) {
unsigned int ticks;
ticks = efx_usecs_to_ticks(efx, channel->irq_moderation_us);
EFX_POPULATE_DWORD_2(timer_cmd, EFX_POPULATE_DWORD_2(timer_cmd,
FRF_AB_TC_TIMER_MODE, FRF_AB_TC_TIMER_MODE,
FFE_BB_TIMER_MODE_INT_HLDOFF, FFE_BB_TIMER_MODE_INT_HLDOFF,
FRF_AB_TC_TIMER_VAL, FRF_AB_TC_TIMER_VAL,
channel->irq_moderation - 1); ticks - 1);
} else { } else {
EFX_POPULATE_DWORD_2(timer_cmd, EFX_POPULATE_DWORD_2(timer_cmd,
FRF_AB_TC_TIMER_MODE, FRF_AB_TC_TIMER_MODE,
......
...@@ -392,7 +392,7 @@ enum efx_sync_events_state { ...@@ -392,7 +392,7 @@ enum efx_sync_events_state {
* @eventq_init: Event queue initialised flag * @eventq_init: Event queue initialised flag
* @enabled: Channel enabled indicator * @enabled: Channel enabled indicator
* @irq: IRQ number (MSI and MSI-X only) * @irq: IRQ number (MSI and MSI-X only)
* @irq_moderation: IRQ moderation value (in hardware ticks) * @irq_moderation_us: IRQ moderation value (in microseconds)
* @napi_dev: Net device used with NAPI * @napi_dev: Net device used with NAPI
* @napi_str: NAPI control structure * @napi_str: NAPI control structure
* @state: state for NAPI vs busy polling * @state: state for NAPI vs busy polling
...@@ -433,7 +433,7 @@ struct efx_channel { ...@@ -433,7 +433,7 @@ struct efx_channel {
bool eventq_init; bool eventq_init;
bool enabled; bool enabled;
int irq; int irq;
unsigned int irq_moderation; unsigned int irq_moderation_us;
struct net_device *napi_dev; struct net_device *napi_dev;
struct napi_struct napi_str; struct napi_struct napi_str;
#ifdef CONFIG_NET_RX_BUSY_POLL #ifdef CONFIG_NET_RX_BUSY_POLL
...@@ -811,7 +811,8 @@ struct vfdi_status; ...@@ -811,7 +811,8 @@ struct vfdi_status;
* @interrupt_mode: Interrupt mode * @interrupt_mode: Interrupt mode
* @timer_quantum_ns: Interrupt timer quantum, in nanoseconds * @timer_quantum_ns: Interrupt timer quantum, in nanoseconds
* @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues * @irq_rx_adaptive: Adaptive IRQ moderation enabled for RX event queues
* @irq_rx_moderation: IRQ moderation time for RX event queues * @irq_rx_mod_step_us: Step size for IRQ moderation for RX event queues
* @irq_rx_moderation_us: IRQ moderation time for RX event queues
* @msg_enable: Log message enable flags * @msg_enable: Log message enable flags
* @state: Device state number (%STATE_*). Serialised by the rtnl_lock. * @state: Device state number (%STATE_*). Serialised by the rtnl_lock.
* @reset_pending: Bitmask for pending resets * @reset_pending: Bitmask for pending resets
...@@ -941,7 +942,8 @@ struct efx_nic { ...@@ -941,7 +942,8 @@ struct efx_nic {
enum efx_int_mode interrupt_mode; enum efx_int_mode interrupt_mode;
unsigned int timer_quantum_ns; unsigned int timer_quantum_ns;
bool irq_rx_adaptive; bool irq_rx_adaptive;
unsigned int irq_rx_moderation; unsigned int irq_mod_step_us;
unsigned int irq_rx_moderation_us;
u32 msg_enable; u32 msg_enable;
enum nic_state state; enum nic_state state;
......
...@@ -507,6 +507,7 @@ enum { ...@@ -507,6 +507,7 @@ enum {
* @stats: Hardware statistics * @stats: Hardware statistics
* @workaround_35388: Flag: firmware supports workaround for bug 35388 * @workaround_35388: Flag: firmware supports workaround for bug 35388
* @workaround_26807: Flag: firmware supports workaround for bug 26807 * @workaround_26807: Flag: firmware supports workaround for bug 26807
* @workaround_61265: Flag: firmware supports workaround for bug 61265
* @must_check_datapath_caps: Flag: @datapath_caps needs to be revalidated * @must_check_datapath_caps: Flag: @datapath_caps needs to be revalidated
* after MC reboot * after MC reboot
* @datapath_caps: Capabilities of datapath firmware (FLAGS1 field of * @datapath_caps: Capabilities of datapath firmware (FLAGS1 field of
...@@ -542,6 +543,7 @@ struct efx_ef10_nic_data { ...@@ -542,6 +543,7 @@ struct efx_ef10_nic_data {
u64 stats[EF10_STAT_COUNT]; u64 stats[EF10_STAT_COUNT];
bool workaround_35388; bool workaround_35388;
bool workaround_26807; bool workaround_26807;
bool workaround_61265;
bool must_check_datapath_caps; bool must_check_datapath_caps;
u32 datapath_caps; u32 datapath_caps;
u32 datapath_caps2; u32 datapath_caps2;
......
...@@ -1306,7 +1306,7 @@ static int efx_ptp_probe_channel(struct efx_channel *channel) ...@@ -1306,7 +1306,7 @@ static int efx_ptp_probe_channel(struct efx_channel *channel)
{ {
struct efx_nic *efx = channel->efx; struct efx_nic *efx = channel->efx;
channel->irq_moderation = 0; channel->irq_moderation_us = 0;
channel->rx_queue.core_index = 0; channel->rx_queue.core_index = 0;
return efx_ptp_probe(efx, channel); return efx_ptp_probe(efx, channel);
......
...@@ -34,19 +34,24 @@ static void siena_init_wol(struct efx_nic *efx); ...@@ -34,19 +34,24 @@ static void siena_init_wol(struct efx_nic *efx);
static void siena_push_irq_moderation(struct efx_channel *channel) static void siena_push_irq_moderation(struct efx_channel *channel)
{ {
struct efx_nic *efx = channel->efx;
efx_dword_t timer_cmd; efx_dword_t timer_cmd;
if (channel->irq_moderation) if (channel->irq_moderation_us) {
unsigned int ticks;
ticks = efx_usecs_to_ticks(efx, channel->irq_moderation_us);
EFX_POPULATE_DWORD_2(timer_cmd, EFX_POPULATE_DWORD_2(timer_cmd,
FRF_CZ_TC_TIMER_MODE, FRF_CZ_TC_TIMER_MODE,
FFE_CZ_TIMER_MODE_INT_HLDOFF, FFE_CZ_TIMER_MODE_INT_HLDOFF,
FRF_CZ_TC_TIMER_VAL, FRF_CZ_TC_TIMER_VAL,
channel->irq_moderation - 1); ticks - 1);
else } else {
EFX_POPULATE_DWORD_2(timer_cmd, EFX_POPULATE_DWORD_2(timer_cmd,
FRF_CZ_TC_TIMER_MODE, FRF_CZ_TC_TIMER_MODE,
FFE_CZ_TIMER_MODE_DIS, FFE_CZ_TIMER_MODE_DIS,
FRF_CZ_TC_TIMER_VAL, 0); FRF_CZ_TC_TIMER_VAL, 0);
}
efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0,
channel->channel); channel->channel);
} }
......
...@@ -50,4 +50,8 @@ ...@@ -50,4 +50,8 @@
#define EFX_WORKAROUND_35388(efx) \ #define EFX_WORKAROUND_35388(efx) \
(efx_nic_rev(efx) == EFX_REV_HUNT_A0 && EFX_EF10_WORKAROUND_35388(efx)) (efx_nic_rev(efx) == EFX_REV_HUNT_A0 && EFX_EF10_WORKAROUND_35388(efx))
/* Moderation timer access must go through MCDI */
#define EFX_EF10_WORKAROUND_61265(efx) \
(((struct efx_ef10_nic_data *)efx->nic_data)->workaround_61265)
#endif /* EFX_WORKAROUNDS_H */ #endif /* EFX_WORKAROUNDS_H */
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