Commit 9d32e4e7 authored by Yinjun Zhang's avatar Yinjun Zhang Committed by David S. Miller

nfp: add support for coalesce adaptive feature

Use dynamic interrupt moderation library to implement coalesce
adaptive feature for nfp driver.
Signed-off-by: default avatarYinjun Zhang <yinjun.zhang@corigine.com>
Signed-off-by: default avatarYu Xiao <yu.xiao@corigine.com>
Signed-off-by: default avatarSimon Horman <simon.horman@corigine.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e129f6b5
......@@ -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))
return -EINVAL;
if (ec->tx_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->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,
......
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