be2net: set interrupt moderation for Skyhawk-R using EQ-DB
Currently adaptive interrupt moderation is set by calculating and configuring an EQ-delay every second. This is done via a FW-cmd. But, on Skyhawk-R a "re-arm to interrupt" delay can be set while ringing the EQ-DB. This patch uses this facility to calculate and set the interrupt delay every 1ms. This helps moderating interrupts better when the traffic is bursty. Signed-off-by: Padmanabh Ratnakar <padmanabh.ratnakar@avagotech.com> Signed-off-by: Sathya Perla <sathya.perla@avagotech.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
e7bcbd7b81
commit
2094777041
@@ -805,6 +805,7 @@ bool be_pause_supported(struct be_adapter *adapter);
|
|||||||
u32 be_get_fw_log_level(struct be_adapter *adapter);
|
u32 be_get_fw_log_level(struct be_adapter *adapter);
|
||||||
int be_update_queues(struct be_adapter *adapter);
|
int be_update_queues(struct be_adapter *adapter);
|
||||||
int be_poll(struct napi_struct *napi, int budget);
|
int be_poll(struct napi_struct *napi, int budget);
|
||||||
|
void be_eqd_update(struct be_adapter *adapter, bool force_update);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* internal function to initialize-cleanup roce device.
|
* internal function to initialize-cleanup roce device.
|
||||||
|
@@ -368,6 +368,14 @@ static int be_set_coalesce(struct net_device *netdev,
|
|||||||
aic++;
|
aic++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For Skyhawk, the EQD setting happens via EQ_DB when AIC is enabled.
|
||||||
|
* When AIC is disabled, persistently force set EQD value via the
|
||||||
|
* FW cmd, so that we don't have to calculate the delay multiplier
|
||||||
|
* encode value each time EQ_DB is rung
|
||||||
|
*/
|
||||||
|
if (!et->use_adaptive_rx_coalesce && skyhawk_chip(adapter))
|
||||||
|
be_eqd_update(adapter, true);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -132,6 +132,18 @@
|
|||||||
#define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */
|
#define DB_EQ_NUM_POPPED_SHIFT (16) /* bits 16 - 28 */
|
||||||
/* Rearm bit */
|
/* Rearm bit */
|
||||||
#define DB_EQ_REARM_SHIFT (29) /* bit 29 */
|
#define DB_EQ_REARM_SHIFT (29) /* bit 29 */
|
||||||
|
/* Rearm to interrupt delay encoding */
|
||||||
|
#define DB_EQ_R2I_DLY_SHIFT (30) /* bits 30 - 31 */
|
||||||
|
|
||||||
|
/* Rearm to interrupt (R2I) delay multiplier encoding represents 3 different
|
||||||
|
* values configured in CEV_REARM2IRPT_DLY_MULT_CSR register. This value is
|
||||||
|
* programmed by host driver while ringing an EQ doorbell(EQ_DB) if a delay
|
||||||
|
* between rearming the EQ and next interrupt on this EQ is desired.
|
||||||
|
*/
|
||||||
|
#define R2I_DLY_ENC_0 0 /* No delay */
|
||||||
|
#define R2I_DLY_ENC_1 1 /* maps to 160us EQ delay */
|
||||||
|
#define R2I_DLY_ENC_2 2 /* maps to 96us EQ delay */
|
||||||
|
#define R2I_DLY_ENC_3 3 /* maps to 48us EQ delay */
|
||||||
|
|
||||||
/********* Compl Q door bell *************/
|
/********* Compl Q door bell *************/
|
||||||
#define DB_CQ_OFFSET 0x120
|
#define DB_CQ_OFFSET 0x120
|
||||||
|
@@ -211,7 +211,8 @@ static void be_txq_notify(struct be_adapter *adapter, struct be_tx_obj *txo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void be_eq_notify(struct be_adapter *adapter, u16 qid,
|
static void be_eq_notify(struct be_adapter *adapter, u16 qid,
|
||||||
bool arm, bool clear_int, u16 num_popped)
|
bool arm, bool clear_int, u16 num_popped,
|
||||||
|
u32 eq_delay_mult_enc)
|
||||||
{
|
{
|
||||||
u32 val = 0;
|
u32 val = 0;
|
||||||
|
|
||||||
@@ -227,6 +228,7 @@ static void be_eq_notify(struct be_adapter *adapter, u16 qid,
|
|||||||
val |= 1 << DB_EQ_CLR_SHIFT;
|
val |= 1 << DB_EQ_CLR_SHIFT;
|
||||||
val |= 1 << DB_EQ_EVNT_SHIFT;
|
val |= 1 << DB_EQ_EVNT_SHIFT;
|
||||||
val |= num_popped << DB_EQ_NUM_POPPED_SHIFT;
|
val |= num_popped << DB_EQ_NUM_POPPED_SHIFT;
|
||||||
|
val |= eq_delay_mult_enc << DB_EQ_R2I_DLY_SHIFT;
|
||||||
iowrite32(val, adapter->db + DB_EQ_OFFSET);
|
iowrite32(val, adapter->db + DB_EQ_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1686,61 +1688,110 @@ static void be_aic_update(struct be_aic_obj *aic, u64 rx_pkts, u64 tx_pkts,
|
|||||||
aic->jiffies = now;
|
aic->jiffies = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void be_eqd_update(struct be_adapter *adapter)
|
static int be_get_new_eqd(struct be_eq_obj *eqo)
|
||||||
{
|
{
|
||||||
struct be_set_eqd set_eqd[MAX_EVT_QS];
|
struct be_adapter *adapter = eqo->adapter;
|
||||||
int eqd, i, num = 0, start;
|
int eqd, start;
|
||||||
struct be_aic_obj *aic;
|
struct be_aic_obj *aic;
|
||||||
struct be_eq_obj *eqo;
|
|
||||||
struct be_rx_obj *rxo;
|
struct be_rx_obj *rxo;
|
||||||
struct be_tx_obj *txo;
|
struct be_tx_obj *txo;
|
||||||
u64 rx_pkts, tx_pkts;
|
u64 rx_pkts = 0, tx_pkts = 0;
|
||||||
ulong now;
|
ulong now;
|
||||||
u32 pps, delta;
|
u32 pps, delta;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
aic = &adapter->aic_obj[eqo->idx];
|
||||||
|
if (!aic->enable) {
|
||||||
|
if (aic->jiffies)
|
||||||
|
aic->jiffies = 0;
|
||||||
|
eqd = aic->et_eqd;
|
||||||
|
return eqd;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_all_rx_queues_on_eq(adapter, eqo, rxo, i) {
|
||||||
|
do {
|
||||||
|
start = u64_stats_fetch_begin_irq(&rxo->stats.sync);
|
||||||
|
rx_pkts += rxo->stats.rx_pkts;
|
||||||
|
} while (u64_stats_fetch_retry_irq(&rxo->stats.sync, start));
|
||||||
|
}
|
||||||
|
|
||||||
|
for_all_tx_queues_on_eq(adapter, eqo, txo, i) {
|
||||||
|
do {
|
||||||
|
start = u64_stats_fetch_begin_irq(&txo->stats.sync);
|
||||||
|
tx_pkts += txo->stats.tx_reqs;
|
||||||
|
} while (u64_stats_fetch_retry_irq(&txo->stats.sync, start));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip, if wrapped around or first calculation */
|
||||||
|
now = jiffies;
|
||||||
|
if (!aic->jiffies || time_before(now, aic->jiffies) ||
|
||||||
|
rx_pkts < aic->rx_pkts_prev ||
|
||||||
|
tx_pkts < aic->tx_reqs_prev) {
|
||||||
|
be_aic_update(aic, rx_pkts, tx_pkts, now);
|
||||||
|
return aic->prev_eqd;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta = jiffies_to_msecs(now - aic->jiffies);
|
||||||
|
if (delta == 0)
|
||||||
|
return aic->prev_eqd;
|
||||||
|
|
||||||
|
pps = (((u32)(rx_pkts - aic->rx_pkts_prev) * 1000) / delta) +
|
||||||
|
(((u32)(tx_pkts - aic->tx_reqs_prev) * 1000) / delta);
|
||||||
|
eqd = (pps / 15000) << 2;
|
||||||
|
|
||||||
|
if (eqd < 8)
|
||||||
|
eqd = 0;
|
||||||
|
eqd = min_t(u32, eqd, aic->max_eqd);
|
||||||
|
eqd = max_t(u32, eqd, aic->min_eqd);
|
||||||
|
|
||||||
|
be_aic_update(aic, rx_pkts, tx_pkts, now);
|
||||||
|
|
||||||
|
return eqd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Skyhawk-R only */
|
||||||
|
static u32 be_get_eq_delay_mult_enc(struct be_eq_obj *eqo)
|
||||||
|
{
|
||||||
|
struct be_adapter *adapter = eqo->adapter;
|
||||||
|
struct be_aic_obj *aic = &adapter->aic_obj[eqo->idx];
|
||||||
|
ulong now = jiffies;
|
||||||
|
int eqd;
|
||||||
|
u32 mult_enc;
|
||||||
|
|
||||||
|
if (!aic->enable)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (time_before_eq(now, aic->jiffies) ||
|
||||||
|
jiffies_to_msecs(now - aic->jiffies) < 1)
|
||||||
|
eqd = aic->prev_eqd;
|
||||||
|
else
|
||||||
|
eqd = be_get_new_eqd(eqo);
|
||||||
|
|
||||||
|
if (eqd > 100)
|
||||||
|
mult_enc = R2I_DLY_ENC_1;
|
||||||
|
else if (eqd > 60)
|
||||||
|
mult_enc = R2I_DLY_ENC_2;
|
||||||
|
else if (eqd > 20)
|
||||||
|
mult_enc = R2I_DLY_ENC_3;
|
||||||
|
else
|
||||||
|
mult_enc = R2I_DLY_ENC_0;
|
||||||
|
|
||||||
|
aic->prev_eqd = eqd;
|
||||||
|
|
||||||
|
return mult_enc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void be_eqd_update(struct be_adapter *adapter, bool force_update)
|
||||||
|
{
|
||||||
|
struct be_set_eqd set_eqd[MAX_EVT_QS];
|
||||||
|
struct be_aic_obj *aic;
|
||||||
|
struct be_eq_obj *eqo;
|
||||||
|
int i, num = 0, eqd;
|
||||||
|
|
||||||
for_all_evt_queues(adapter, eqo, i) {
|
for_all_evt_queues(adapter, eqo, i) {
|
||||||
aic = &adapter->aic_obj[eqo->idx];
|
aic = &adapter->aic_obj[eqo->idx];
|
||||||
if (!aic->enable) {
|
eqd = be_get_new_eqd(eqo);
|
||||||
if (aic->jiffies)
|
if (force_update || eqd != aic->prev_eqd) {
|
||||||
aic->jiffies = 0;
|
|
||||||
eqd = aic->et_eqd;
|
|
||||||
goto modify_eqd;
|
|
||||||
}
|
|
||||||
|
|
||||||
rxo = &adapter->rx_obj[eqo->idx];
|
|
||||||
do {
|
|
||||||
start = u64_stats_fetch_begin_irq(&rxo->stats.sync);
|
|
||||||
rx_pkts = rxo->stats.rx_pkts;
|
|
||||||
} while (u64_stats_fetch_retry_irq(&rxo->stats.sync, start));
|
|
||||||
|
|
||||||
txo = &adapter->tx_obj[eqo->idx];
|
|
||||||
do {
|
|
||||||
start = u64_stats_fetch_begin_irq(&txo->stats.sync);
|
|
||||||
tx_pkts = txo->stats.tx_reqs;
|
|
||||||
} while (u64_stats_fetch_retry_irq(&txo->stats.sync, start));
|
|
||||||
|
|
||||||
/* Skip, if wrapped around or first calculation */
|
|
||||||
now = jiffies;
|
|
||||||
if (!aic->jiffies || time_before(now, aic->jiffies) ||
|
|
||||||
rx_pkts < aic->rx_pkts_prev ||
|
|
||||||
tx_pkts < aic->tx_reqs_prev) {
|
|
||||||
be_aic_update(aic, rx_pkts, tx_pkts, now);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
delta = jiffies_to_msecs(now - aic->jiffies);
|
|
||||||
pps = (((u32)(rx_pkts - aic->rx_pkts_prev) * 1000) / delta) +
|
|
||||||
(((u32)(tx_pkts - aic->tx_reqs_prev) * 1000) / delta);
|
|
||||||
eqd = (pps / 15000) << 2;
|
|
||||||
|
|
||||||
if (eqd < 8)
|
|
||||||
eqd = 0;
|
|
||||||
eqd = min_t(u32, eqd, aic->max_eqd);
|
|
||||||
eqd = max_t(u32, eqd, aic->min_eqd);
|
|
||||||
|
|
||||||
be_aic_update(aic, rx_pkts, tx_pkts, now);
|
|
||||||
modify_eqd:
|
|
||||||
if (eqd != aic->prev_eqd) {
|
|
||||||
set_eqd[num].delay_multiplier = (eqd * 65)/100;
|
set_eqd[num].delay_multiplier = (eqd * 65)/100;
|
||||||
set_eqd[num].eq_id = eqo->q.id;
|
set_eqd[num].eq_id = eqo->q.id;
|
||||||
aic->prev_eqd = eqd;
|
aic->prev_eqd = eqd;
|
||||||
@@ -2248,7 +2299,7 @@ static void be_eq_clean(struct be_eq_obj *eqo)
|
|||||||
{
|
{
|
||||||
int num = events_get(eqo);
|
int num = events_get(eqo);
|
||||||
|
|
||||||
be_eq_notify(eqo->adapter, eqo->q.id, false, true, num);
|
be_eq_notify(eqo->adapter, eqo->q.id, false, true, num, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void be_rx_cq_clean(struct be_rx_obj *rxo)
|
static void be_rx_cq_clean(struct be_rx_obj *rxo)
|
||||||
@@ -2609,7 +2660,7 @@ static irqreturn_t be_intx(int irq, void *dev)
|
|||||||
if (num_evts)
|
if (num_evts)
|
||||||
eqo->spurious_intr = 0;
|
eqo->spurious_intr = 0;
|
||||||
}
|
}
|
||||||
be_eq_notify(adapter, eqo->q.id, false, true, num_evts);
|
be_eq_notify(adapter, eqo->q.id, false, true, num_evts, 0);
|
||||||
|
|
||||||
/* Return IRQ_HANDLED only for the the first spurious intr
|
/* Return IRQ_HANDLED only for the the first spurious intr
|
||||||
* after a valid intr to stop the kernel from branding
|
* after a valid intr to stop the kernel from branding
|
||||||
@@ -2625,7 +2676,7 @@ static irqreturn_t be_msix(int irq, void *dev)
|
|||||||
{
|
{
|
||||||
struct be_eq_obj *eqo = dev;
|
struct be_eq_obj *eqo = dev;
|
||||||
|
|
||||||
be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0);
|
be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0, 0);
|
||||||
napi_schedule(&eqo->napi);
|
napi_schedule(&eqo->napi);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
@@ -2874,6 +2925,7 @@ int be_poll(struct napi_struct *napi, int budget)
|
|||||||
int max_work = 0, work, i, num_evts;
|
int max_work = 0, work, i, num_evts;
|
||||||
struct be_rx_obj *rxo;
|
struct be_rx_obj *rxo;
|
||||||
struct be_tx_obj *txo;
|
struct be_tx_obj *txo;
|
||||||
|
u32 mult_enc = 0;
|
||||||
|
|
||||||
num_evts = events_get(eqo);
|
num_evts = events_get(eqo);
|
||||||
|
|
||||||
@@ -2899,10 +2951,18 @@ int be_poll(struct napi_struct *napi, int budget)
|
|||||||
|
|
||||||
if (max_work < budget) {
|
if (max_work < budget) {
|
||||||
napi_complete(napi);
|
napi_complete(napi);
|
||||||
be_eq_notify(adapter, eqo->q.id, true, false, num_evts);
|
|
||||||
|
/* Skyhawk EQ_DB has a provision to set the rearm to interrupt
|
||||||
|
* delay via a delay multiplier encoding value
|
||||||
|
*/
|
||||||
|
if (skyhawk_chip(adapter))
|
||||||
|
mult_enc = be_get_eq_delay_mult_enc(eqo);
|
||||||
|
|
||||||
|
be_eq_notify(adapter, eqo->q.id, true, false, num_evts,
|
||||||
|
mult_enc);
|
||||||
} else {
|
} else {
|
||||||
/* As we'll continue in polling mode, count and clear events */
|
/* As we'll continue in polling mode, count and clear events */
|
||||||
be_eq_notify(adapter, eqo->q.id, false, false, num_evts);
|
be_eq_notify(adapter, eqo->q.id, false, false, num_evts, 0);
|
||||||
}
|
}
|
||||||
return max_work;
|
return max_work;
|
||||||
}
|
}
|
||||||
@@ -3299,7 +3359,7 @@ static int be_open(struct net_device *netdev)
|
|||||||
for_all_evt_queues(adapter, eqo, i) {
|
for_all_evt_queues(adapter, eqo, i) {
|
||||||
napi_enable(&eqo->napi);
|
napi_enable(&eqo->napi);
|
||||||
be_enable_busy_poll(eqo);
|
be_enable_busy_poll(eqo);
|
||||||
be_eq_notify(adapter, eqo->q.id, true, true, 0);
|
be_eq_notify(adapter, eqo->q.id, true, true, 0, 0);
|
||||||
}
|
}
|
||||||
adapter->flags |= BE_FLAGS_NAPI_ENABLED;
|
adapter->flags |= BE_FLAGS_NAPI_ENABLED;
|
||||||
|
|
||||||
@@ -4225,7 +4285,7 @@ static void be_netpoll(struct net_device *netdev)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for_all_evt_queues(adapter, eqo, i) {
|
for_all_evt_queues(adapter, eqo, i) {
|
||||||
be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0);
|
be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0, 0);
|
||||||
napi_schedule(&eqo->napi);
|
napi_schedule(&eqo->napi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5227,7 +5287,9 @@ static void be_worker(struct work_struct *work)
|
|||||||
be_post_rx_frags(rxo, GFP_KERNEL, MAX_RX_POST);
|
be_post_rx_frags(rxo, GFP_KERNEL, MAX_RX_POST);
|
||||||
}
|
}
|
||||||
|
|
||||||
be_eqd_update(adapter);
|
/* EQ-delay update for Skyhawk is done while notifying EQ */
|
||||||
|
if (!skyhawk_chip(adapter))
|
||||||
|
be_eqd_update(adapter, false);
|
||||||
|
|
||||||
if (adapter->flags & BE_FLAGS_EVT_INCOMPATIBLE_SFP)
|
if (adapter->flags & BE_FLAGS_EVT_INCOMPATIBLE_SFP)
|
||||||
be_log_sfp_info(adapter);
|
be_log_sfp_info(adapter);
|
||||||
|
Reference in New Issue
Block a user