nfp: add support for coalesce adaptive feature
Use dynamic interrupt moderation library to implement coalesce adaptive feature for nfp driver. Signed-off-by: Yinjun Zhang <yinjun.zhang@corigine.com> Signed-off-by: Yu Xiao <yu.xiao@corigine.com> Signed-off-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e129f6b5ae
commit
9d32e4e7e9
|
@ -23,6 +23,7 @@ config NFP
|
|||
depends on TLS && TLS_DEVICE || TLS_DEVICE=n
|
||||
select NET_DEVLINK
|
||||
select CRC32
|
||||
select DIMLIB
|
||||
help
|
||||
This driver supports the Netronome(R) NFP4000/NFP6000 based
|
||||
cards working as a advanced Ethernet NIC. It works with both
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/dim.h>
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
@ -360,6 +361,9 @@ struct nfp_net_rx_ring {
|
|||
* @rx_ring: Pointer to RX ring
|
||||
* @xdp_ring: Pointer to an extra TX ring for XDP
|
||||
* @irq_entry: MSI-X table entry (use for talking to the device)
|
||||
* @event_ctr: Number of interrupt
|
||||
* @rx_dim: Dynamic interrupt moderation structure for RX
|
||||
* @tx_dim: Dynamic interrupt moderation structure for TX
|
||||
* @rx_sync: Seqlock for atomic updates of RX stats
|
||||
* @rx_pkts: Number of received packets
|
||||
* @rx_bytes: Number of received bytes
|
||||
|
@ -410,6 +414,10 @@ struct nfp_net_r_vector {
|
|||
|
||||
u16 irq_entry;
|
||||
|
||||
u16 event_ctr;
|
||||
struct dim rx_dim;
|
||||
struct dim tx_dim;
|
||||
|
||||
struct u64_stats_sync rx_sync;
|
||||
u64 rx_pkts;
|
||||
u64 rx_bytes;
|
||||
|
@ -571,6 +579,8 @@ struct nfp_net_dp {
|
|||
* mailbox area, crypto TLV
|
||||
* @link_up: Is the link up?
|
||||
* @link_status_lock: Protects @link_* and ensures atomicity with BAR reading
|
||||
* @rx_coalesce_adapt_on: Is RX interrupt moderation adaptive?
|
||||
* @tx_coalesce_adapt_on: Is TX interrupt moderation adaptive?
|
||||
* @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter
|
||||
* @rx_coalesce_max_frames: RX interrupt moderation frame count parameter
|
||||
* @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter
|
||||
|
@ -654,6 +664,8 @@ struct nfp_net {
|
|||
|
||||
struct semaphore bar_lock;
|
||||
|
||||
bool rx_coalesce_adapt_on;
|
||||
bool tx_coalesce_adapt_on;
|
||||
u32 rx_coalesce_usecs;
|
||||
u32 rx_coalesce_max_frames;
|
||||
u32 tx_coalesce_usecs;
|
||||
|
@ -919,6 +931,14 @@ static inline bool nfp_netdev_is_nfp_net(struct net_device *netdev)
|
|||
return netdev->netdev_ops == &nfp_net_netdev_ops;
|
||||
}
|
||||
|
||||
static inline int nfp_net_coalesce_para_check(u32 usecs, u32 pkts)
|
||||
{
|
||||
if ((usecs >= ((1 << 16) - 1)) || (pkts >= ((1 << 16) - 1)))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prototypes */
|
||||
void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
|
||||
void __iomem *ctrl_bar);
|
||||
|
|
|
@ -474,6 +474,12 @@ static irqreturn_t nfp_net_irq_rxtx(int irq, void *data)
|
|||
{
|
||||
struct nfp_net_r_vector *r_vec = data;
|
||||
|
||||
/* Currently we cannot tell if it's a rx or tx interrupt,
|
||||
* since dim does not need accurate event_ctr to calculate,
|
||||
* we just use this counter for both rx and tx dim.
|
||||
*/
|
||||
r_vec->event_ctr++;
|
||||
|
||||
napi_schedule_irqoff(&r_vec->napi);
|
||||
|
||||
/* The FW auto-masks any interrupt, either via the MASK bit in
|
||||
|
@ -2061,6 +2067,36 @@ static int nfp_net_poll(struct napi_struct *napi, int budget)
|
|||
if (napi_complete_done(napi, pkts_polled))
|
||||
nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry);
|
||||
|
||||
if (r_vec->nfp_net->rx_coalesce_adapt_on) {
|
||||
struct dim_sample dim_sample = {};
|
||||
unsigned int start;
|
||||
u64 pkts, bytes;
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&r_vec->rx_sync);
|
||||
pkts = r_vec->rx_pkts;
|
||||
bytes = r_vec->rx_bytes;
|
||||
} while (u64_stats_fetch_retry(&r_vec->rx_sync, start));
|
||||
|
||||
dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample);
|
||||
net_dim(&r_vec->rx_dim, dim_sample);
|
||||
}
|
||||
|
||||
if (r_vec->nfp_net->tx_coalesce_adapt_on) {
|
||||
struct dim_sample dim_sample = {};
|
||||
unsigned int start;
|
||||
u64 pkts, bytes;
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&r_vec->tx_sync);
|
||||
pkts = r_vec->tx_pkts;
|
||||
bytes = r_vec->tx_bytes;
|
||||
} while (u64_stats_fetch_retry(&r_vec->tx_sync, start));
|
||||
|
||||
dim_update_sample(r_vec->event_ctr, pkts, bytes, &dim_sample);
|
||||
net_dim(&r_vec->tx_dim, dim_sample);
|
||||
}
|
||||
|
||||
return pkts_polled;
|
||||
}
|
||||
|
||||
|
@ -2873,6 +2909,7 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn)
|
|||
*/
|
||||
static void nfp_net_close_stack(struct nfp_net *nn)
|
||||
{
|
||||
struct nfp_net_r_vector *r_vec;
|
||||
unsigned int r;
|
||||
|
||||
disable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector);
|
||||
|
@ -2880,8 +2917,16 @@ static void nfp_net_close_stack(struct nfp_net *nn)
|
|||
nn->link_up = false;
|
||||
|
||||
for (r = 0; r < nn->dp.num_r_vecs; r++) {
|
||||
disable_irq(nn->r_vecs[r].irq_vector);
|
||||
napi_disable(&nn->r_vecs[r].napi);
|
||||
r_vec = &nn->r_vecs[r];
|
||||
|
||||
disable_irq(r_vec->irq_vector);
|
||||
napi_disable(&r_vec->napi);
|
||||
|
||||
if (r_vec->rx_ring)
|
||||
cancel_work_sync(&r_vec->rx_dim.work);
|
||||
|
||||
if (r_vec->tx_ring)
|
||||
cancel_work_sync(&r_vec->tx_dim.work);
|
||||
}
|
||||
|
||||
netif_tx_disable(nn->dp.netdev);
|
||||
|
@ -2948,17 +2993,92 @@ void nfp_ctrl_close(struct nfp_net *nn)
|
|||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static void nfp_net_rx_dim_work(struct work_struct *work)
|
||||
{
|
||||
struct nfp_net_r_vector *r_vec;
|
||||
unsigned int factor, value;
|
||||
struct dim_cq_moder moder;
|
||||
struct nfp_net *nn;
|
||||
struct dim *dim;
|
||||
|
||||
dim = container_of(work, struct dim, work);
|
||||
moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
|
||||
r_vec = container_of(dim, struct nfp_net_r_vector, rx_dim);
|
||||
nn = r_vec->nfp_net;
|
||||
|
||||
/* Compute factor used to convert coalesce '_usecs' parameters to
|
||||
* ME timestamp ticks. There are 16 ME clock cycles for each timestamp
|
||||
* count.
|
||||
*/
|
||||
factor = nn->tlv_caps.me_freq_mhz / 16;
|
||||
if (nfp_net_coalesce_para_check(factor * moder.usec, moder.pkts))
|
||||
return;
|
||||
|
||||
/* copy RX interrupt coalesce parameters */
|
||||
value = (moder.pkts << 16) | (factor * moder.usec);
|
||||
rtnl_lock();
|
||||
nn_writel(nn, NFP_NET_CFG_RXR_IRQ_MOD(r_vec->rx_ring->idx), value);
|
||||
(void)nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD);
|
||||
rtnl_unlock();
|
||||
|
||||
dim->state = DIM_START_MEASURE;
|
||||
}
|
||||
|
||||
static void nfp_net_tx_dim_work(struct work_struct *work)
|
||||
{
|
||||
struct nfp_net_r_vector *r_vec;
|
||||
unsigned int factor, value;
|
||||
struct dim_cq_moder moder;
|
||||
struct nfp_net *nn;
|
||||
struct dim *dim;
|
||||
|
||||
dim = container_of(work, struct dim, work);
|
||||
moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix);
|
||||
r_vec = container_of(dim, struct nfp_net_r_vector, tx_dim);
|
||||
nn = r_vec->nfp_net;
|
||||
|
||||
/* Compute factor used to convert coalesce '_usecs' parameters to
|
||||
* ME timestamp ticks. There are 16 ME clock cycles for each timestamp
|
||||
* count.
|
||||
*/
|
||||
factor = nn->tlv_caps.me_freq_mhz / 16;
|
||||
if (nfp_net_coalesce_para_check(factor * moder.usec, moder.pkts))
|
||||
return;
|
||||
|
||||
/* copy TX interrupt coalesce parameters */
|
||||
value = (moder.pkts << 16) | (factor * moder.usec);
|
||||
rtnl_lock();
|
||||
nn_writel(nn, NFP_NET_CFG_TXR_IRQ_MOD(r_vec->tx_ring->idx), value);
|
||||
(void)nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD);
|
||||
rtnl_unlock();
|
||||
|
||||
dim->state = DIM_START_MEASURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfp_net_open_stack() - Start the device from stack's perspective
|
||||
* @nn: NFP Net device to reconfigure
|
||||
*/
|
||||
static void nfp_net_open_stack(struct nfp_net *nn)
|
||||
{
|
||||
struct nfp_net_r_vector *r_vec;
|
||||
unsigned int r;
|
||||
|
||||
for (r = 0; r < nn->dp.num_r_vecs; r++) {
|
||||
napi_enable(&nn->r_vecs[r].napi);
|
||||
enable_irq(nn->r_vecs[r].irq_vector);
|
||||
r_vec = &nn->r_vecs[r];
|
||||
|
||||
if (r_vec->rx_ring) {
|
||||
INIT_WORK(&r_vec->rx_dim.work, nfp_net_rx_dim_work);
|
||||
r_vec->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
|
||||
}
|
||||
|
||||
if (r_vec->tx_ring) {
|
||||
INIT_WORK(&r_vec->tx_dim.work, nfp_net_tx_dim_work);
|
||||
r_vec->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
|
||||
}
|
||||
|
||||
napi_enable(&r_vec->napi);
|
||||
enable_irq(r_vec->irq_vector);
|
||||
}
|
||||
|
||||
netif_tx_wake_all_queues(nn->dp.netdev);
|
||||
|
@ -3893,6 +4013,9 @@ static void nfp_net_irqmod_init(struct nfp_net *nn)
|
|||
nn->rx_coalesce_max_frames = 64;
|
||||
nn->tx_coalesce_usecs = 50;
|
||||
nn->tx_coalesce_max_frames = 64;
|
||||
|
||||
nn->rx_coalesce_adapt_on = true;
|
||||
nn->tx_coalesce_adapt_on = true;
|
||||
}
|
||||
|
||||
static void nfp_net_netdev_init(struct nfp_net *nn)
|
||||
|
|
|
@ -1083,6 +1083,9 @@ static int nfp_net_get_coalesce(struct net_device *netdev,
|
|||
if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD))
|
||||
return -EINVAL;
|
||||
|
||||
ec->use_adaptive_rx_coalesce = nn->rx_coalesce_adapt_on;
|
||||
ec->use_adaptive_tx_coalesce = nn->tx_coalesce_adapt_on;
|
||||
|
||||
ec->rx_coalesce_usecs = nn->rx_coalesce_usecs;
|
||||
ec->rx_max_coalesced_frames = nn->rx_coalesce_max_frames;
|
||||
ec->tx_coalesce_usecs = nn->tx_coalesce_usecs;
|
||||
|
@ -1359,19 +1362,18 @@ static int nfp_net_set_coalesce(struct net_device *netdev,
|
|||
if (!ec->tx_coalesce_usecs && !ec->tx_max_coalesced_frames)
|
||||
return -EINVAL;
|
||||
|
||||
if (ec->rx_coalesce_usecs * factor >= ((1 << 16) - 1))
|
||||
if (nfp_net_coalesce_para_check(ec->rx_coalesce_usecs * factor,
|
||||
ec->rx_max_coalesced_frames))
|
||||
return -EINVAL;
|
||||
|
||||
if (ec->tx_coalesce_usecs * factor >= ((1 << 16) - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (ec->rx_max_coalesced_frames >= ((1 << 16) - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (ec->tx_max_coalesced_frames >= ((1 << 16) - 1))
|
||||
if (nfp_net_coalesce_para_check(ec->tx_coalesce_usecs * factor,
|
||||
ec->tx_max_coalesced_frames))
|
||||
return -EINVAL;
|
||||
|
||||
/* configuration is valid */
|
||||
nn->rx_coalesce_adapt_on = !!ec->use_adaptive_rx_coalesce;
|
||||
nn->tx_coalesce_adapt_on = !!ec->use_adaptive_tx_coalesce;
|
||||
|
||||
nn->rx_coalesce_usecs = ec->rx_coalesce_usecs;
|
||||
nn->rx_coalesce_max_frames = ec->rx_max_coalesced_frames;
|
||||
nn->tx_coalesce_usecs = ec->tx_coalesce_usecs;
|
||||
|
@ -1443,7 +1445,8 @@ static int nfp_net_set_channels(struct net_device *netdev,
|
|||
|
||||
static const struct ethtool_ops nfp_net_ethtool_ops = {
|
||||
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
|
||||
ETHTOOL_COALESCE_MAX_FRAMES,
|
||||
ETHTOOL_COALESCE_MAX_FRAMES |
|
||||
ETHTOOL_COALESCE_USE_ADAPTIVE,
|
||||
.get_drvinfo = nfp_net_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ringparam = nfp_net_get_ringparam,
|
||||
|
|
Loading…
Reference in New Issue