提交 566e51d7 编写于 作者: D David S. Miller

Merge branch 'nfp-abm-RED-MQ-qdisc-offload'

Jakub Kicinski says:

====================
nfp: abm: RED/MQ qdisc offload

This is second batch of advanced buffer management nfp driver
changes.  This series adds the qdisc offload.  Support for
a very simple subset of RED qdisc offload is added as needed
for DCTCP ECN marking (min and max thresholds set to the same
value).

The first two patches fix glitches introduced by the previous
series.  We have to be careful about phys_port_name handling,
because VFs share the same code path, and some user space may
get confused by the names we chose.

Since unlike previous offloads we can report the queue backlog
both in bytes and packets we need to adjust how statistics are
added up in the core (patch 6).

There are some extra statistics we want to expose which don't
fit into TC stats, namely counts of packets which have been fast-
-forwarded without getting enqueued because there was no
contention and number of packets that were ever queued (sum of
all momentary backlogs).  We expose those through ethtool stats
(patches 8 and 9).

Remaining 5 patches add MQ offload - to be able to set different
configurations on different queues.  Representors are made multi-
-queue and we add offload support to MQ.  MQ stats are added up
before calling ->dump qdiscs on the children, and therefore don't
include updated offload values.  To avoid clearly incorrect stats
MQ is made to also request stats update from offloads.  This way
we can correct the diff at the driver level.
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
......@@ -35,24 +35,299 @@
#include <linux/kernel.h>
#include "../nfpcore/nfp_cpp.h"
#include "../nfpcore/nfp_nffw.h"
#include "../nfp_app.h"
#include "../nfp_abi.h"
#include "../nfp_main.h"
#include "../nfp_net.h"
#include "main.h"
#define NFP_QLVL_SYM_NAME "_abi_nfd_out_q_lvls_%u"
#define NFP_QLVL_STRIDE 16
#define NFP_QLVL_BLOG_BYTES 0
#define NFP_QLVL_BLOG_PKTS 4
#define NFP_QLVL_THRS 8
#define NFP_QMSTAT_SYM_NAME "_abi_nfdqm%u_stats"
#define NFP_QMSTAT_STRIDE 32
#define NFP_QMSTAT_NON_STO 0
#define NFP_QMSTAT_STO 8
#define NFP_QMSTAT_DROP 16
#define NFP_QMSTAT_ECN 24
static unsigned long long
nfp_abm_q_lvl_thrs(struct nfp_abm_link *alink, unsigned int queue)
{
return alink->abm->q_lvls->addr +
(alink->queue_base + queue) * NFP_QLVL_STRIDE + NFP_QLVL_THRS;
}
static int
nfp_abm_ctrl_stat(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
unsigned int stride, unsigned int offset, unsigned int i,
bool is_u64, u64 *res)
{
struct nfp_cpp *cpp = alink->abm->app->cpp;
u32 val32, mur;
u64 val, addr;
int err;
mur = NFP_CPP_ATOMIC_RD(sym->target, sym->domain);
addr = sym->addr + (alink->queue_base + i) * stride + offset;
if (is_u64)
err = nfp_cpp_readq(cpp, mur, addr, &val);
else
err = nfp_cpp_readl(cpp, mur, addr, &val32);
if (err) {
nfp_err(cpp,
"RED offload reading stat failed on vNIC %d queue %d\n",
alink->id, i);
return err;
}
*res = is_u64 ? val : val32;
return 0;
}
static int
nfp_abm_ctrl_stat_all(struct nfp_abm_link *alink, const struct nfp_rtsym *sym,
unsigned int stride, unsigned int offset, bool is_u64,
u64 *res)
{
u64 val, sum = 0;
unsigned int i;
int err;
for (i = 0; i < alink->vnic->max_rx_rings; i++) {
err = nfp_abm_ctrl_stat(alink, sym, stride, offset, i,
is_u64, &val);
if (err)
return err;
sum += val;
}
*res = sum;
return 0;
}
int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int i, u32 val)
{
struct nfp_cpp *cpp = alink->abm->app->cpp;
u32 muw;
int err;
muw = NFP_CPP_ATOMIC_WR(alink->abm->q_lvls->target,
alink->abm->q_lvls->domain);
err = nfp_cpp_writel(cpp, muw, nfp_abm_q_lvl_thrs(alink, i), val);
if (err) {
nfp_err(cpp, "RED offload setting level failed on vNIC %d queue %d\n",
alink->id, i);
return err;
}
return 0;
}
int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val)
{
int i, err;
for (i = 0; i < alink->vnic->max_rx_rings; i++) {
err = nfp_abm_ctrl_set_q_lvl(alink, i, val);
if (err)
return err;
}
return 0;
}
u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i)
{
u64 val;
if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats, NFP_QMSTAT_STRIDE,
NFP_QMSTAT_NON_STO, i, true, &val))
return 0;
return val;
}
u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i)
{
u64 val;
if (nfp_abm_ctrl_stat(alink, alink->abm->qm_stats, NFP_QMSTAT_STRIDE,
NFP_QMSTAT_STO, i, true, &val))
return 0;
return val;
}
int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int i,
struct nfp_alink_stats *stats)
{
int err;
stats->tx_pkts = nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i));
stats->tx_bytes = nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i) + 8);
err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls,
NFP_QLVL_STRIDE, NFP_QLVL_BLOG_BYTES,
i, false, &stats->backlog_bytes);
if (err)
return err;
err = nfp_abm_ctrl_stat(alink, alink->abm->q_lvls,
NFP_QLVL_STRIDE, NFP_QLVL_BLOG_PKTS,
i, false, &stats->backlog_pkts);
if (err)
return err;
err = nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
i, true, &stats->drops);
if (err)
return err;
return nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
i, true, &stats->overlimits);
}
int nfp_abm_ctrl_read_stats(struct nfp_abm_link *alink,
struct nfp_alink_stats *stats)
{
u64 pkts = 0, bytes = 0;
int i, err;
for (i = 0; i < alink->vnic->max_rx_rings; i++) {
pkts += nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i));
bytes += nn_readq(alink->vnic, NFP_NET_CFG_RXR_STATS(i) + 8);
}
stats->tx_pkts = pkts;
stats->tx_bytes = bytes;
err = nfp_abm_ctrl_stat_all(alink, alink->abm->q_lvls,
NFP_QLVL_STRIDE, NFP_QLVL_BLOG_BYTES,
false, &stats->backlog_bytes);
if (err)
return err;
err = nfp_abm_ctrl_stat_all(alink, alink->abm->q_lvls,
NFP_QLVL_STRIDE, NFP_QLVL_BLOG_PKTS,
false, &stats->backlog_pkts);
if (err)
return err;
err = nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
true, &stats->drops);
if (err)
return err;
return nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
true, &stats->overlimits);
}
int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink, unsigned int i,
struct nfp_alink_xstats *xstats)
{
int err;
err = nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
i, true, &xstats->pdrop);
if (err)
return err;
return nfp_abm_ctrl_stat(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
i, true, &xstats->ecn_marked);
}
int nfp_abm_ctrl_read_xstats(struct nfp_abm_link *alink,
struct nfp_alink_xstats *xstats)
{
int err;
err = nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_DROP,
true, &xstats->pdrop);
if (err)
return err;
return nfp_abm_ctrl_stat_all(alink, alink->abm->qm_stats,
NFP_QMSTAT_STRIDE, NFP_QMSTAT_ECN,
true, &xstats->ecn_marked);
}
int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm)
{
return nfp_mbox_cmd(abm->app->pf, NFP_MBOX_PCIE_ABM_ENABLE,
NULL, 0, NULL, 0);
}
int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm)
{
return nfp_mbox_cmd(abm->app->pf, NFP_MBOX_PCIE_ABM_DISABLE,
NULL, 0, NULL, 0);
}
void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink)
{
alink->queue_base = nn_readl(alink->vnic, NFP_NET_CFG_START_RXQ);
alink->queue_base /= alink->vnic->stride_rx;
}
static const struct nfp_rtsym *
nfp_abm_ctrl_find_rtsym(struct nfp_pf *pf, const char *name, unsigned int size)
{
const struct nfp_rtsym *sym;
sym = nfp_rtsym_lookup(pf->rtbl, name);
if (!sym) {
nfp_err(pf->cpp, "Symbol '%s' not found\n", name);
return ERR_PTR(-ENOENT);
}
if (sym->size != size) {
nfp_err(pf->cpp,
"Symbol '%s' wrong size: expected %u got %llu\n",
name, size, sym->size);
return ERR_PTR(-EINVAL);
}
return sym;
}
static const struct nfp_rtsym *
nfp_abm_ctrl_find_q_rtsym(struct nfp_pf *pf, const char *name,
unsigned int size)
{
return nfp_abm_ctrl_find_rtsym(pf, name, size * NFP_NET_MAX_RX_RINGS);
}
int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm)
{
struct nfp_pf *pf = abm->app->pf;
const struct nfp_rtsym *sym;
unsigned int pf_id;
char pf_symbol[64];
pf_id = nfp_cppcore_pcie_unit(pf->cpp);
abm->pf_id = pf_id;
snprintf(pf_symbol, sizeof(pf_symbol), NFP_QLVL_SYM_NAME, pf_id);
sym = nfp_abm_ctrl_find_q_rtsym(pf, pf_symbol, NFP_QLVL_STRIDE);
if (IS_ERR(sym))
return PTR_ERR(sym);
abm->q_lvls = sym;
snprintf(pf_symbol, sizeof(pf_symbol), NFP_QMSTAT_SYM_NAME, pf_id);
sym = nfp_abm_ctrl_find_q_rtsym(pf, pf_symbol, NFP_QMSTAT_STRIDE);
if (IS_ERR(sym))
return PTR_ERR(sym);
abm->qm_stats = sym;
return 0;
}
......@@ -38,6 +38,9 @@
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>
#include <net/red.h>
#include "../nfpcore/nfp.h"
#include "../nfpcore/nfp_cpp.h"
......@@ -55,6 +58,290 @@ static u32 nfp_abm_portid(enum nfp_repr_type rtype, unsigned int id)
FIELD_PREP(NFP_ABM_PORTID_ID, id);
}
static int
__nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
u32 handle, unsigned int qs, u32 init_val)
{
struct nfp_port *port = nfp_port_from_netdev(netdev);
int ret;
ret = nfp_abm_ctrl_set_all_q_lvls(alink, init_val);
memset(alink->qdiscs, 0, sizeof(*alink->qdiscs) * alink->num_qdiscs);
alink->parent = handle;
alink->num_qdiscs = qs;
port->tc_offload_cnt = qs;
return ret;
}
static void
nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
u32 handle, unsigned int qs)
{
__nfp_abm_reset_root(netdev, alink, handle, qs, ~0);
}
static int
nfp_abm_red_find(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
{
unsigned int i = TC_H_MIN(opt->parent) - 1;
if (opt->parent == TC_H_ROOT)
i = 0;
else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent))
i = TC_H_MIN(opt->parent) - 1;
else
return -EOPNOTSUPP;
if (i >= alink->num_qdiscs || opt->handle != alink->qdiscs[i].handle)
return -EOPNOTSUPP;
return i;
}
static void
nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
u32 handle)
{
unsigned int i;
for (i = 0; i < alink->num_qdiscs; i++)
if (handle == alink->qdiscs[i].handle)
break;
if (i == alink->num_qdiscs)
return;
if (alink->parent == TC_H_ROOT) {
nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
} else {
nfp_abm_ctrl_set_q_lvl(alink, i, ~0);
memset(&alink->qdiscs[i], 0, sizeof(*alink->qdiscs));
}
}
static int
nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
struct tc_red_qopt_offload *opt)
{
bool existing;
int i, err;
i = nfp_abm_red_find(alink, opt);
existing = i >= 0;
if (opt->set.min != opt->set.max || !opt->set.is_ecn) {
nfp_warn(alink->abm->app->cpp,
"RED offload failed - unsupported parameters\n");
err = -EINVAL;
goto err_destroy;
}
if (existing) {
if (alink->parent == TC_H_ROOT)
err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min);
else
err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
if (err)
goto err_destroy;
return 0;
}
if (opt->parent == TC_H_ROOT) {
i = 0;
err = __nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 1,
opt->set.min);
} else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) {
i = TC_H_MIN(opt->parent) - 1;
err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
} else {
return -EINVAL;
}
/* Set the handle to try full clean up, in case IO failed */
alink->qdiscs[i].handle = opt->handle;
if (err)
goto err_destroy;
if (opt->parent == TC_H_ROOT)
err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[i].stats);
else
err = nfp_abm_ctrl_read_q_stats(alink, i,
&alink->qdiscs[i].stats);
if (err)
goto err_destroy;
if (opt->parent == TC_H_ROOT)
err = nfp_abm_ctrl_read_xstats(alink,
&alink->qdiscs[i].xstats);
else
err = nfp_abm_ctrl_read_q_xstats(alink, i,
&alink->qdiscs[i].xstats);
if (err)
goto err_destroy;
alink->qdiscs[i].stats.backlog_pkts = 0;
alink->qdiscs[i].stats.backlog_bytes = 0;
return 0;
err_destroy:
/* If the qdisc keeps on living, but we can't offload undo changes */
if (existing) {
opt->set.qstats->qlen -= alink->qdiscs[i].stats.backlog_pkts;
opt->set.qstats->backlog -=
alink->qdiscs[i].stats.backlog_bytes;
}
nfp_abm_red_destroy(netdev, alink, opt->handle);
return err;
}
static void
nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old,
struct tc_qopt_offload_stats *stats)
{
_bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes,
new->tx_pkts - old->tx_pkts);
stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts;
stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes;
stats->qstats->overlimits += new->overlimits - old->overlimits;
stats->qstats->drops += new->drops - old->drops;
}
static int
nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
{
struct nfp_alink_stats *prev_stats;
struct nfp_alink_stats stats;
int i, err;
i = nfp_abm_red_find(alink, opt);
if (i < 0)
return i;
prev_stats = &alink->qdiscs[i].stats;
if (alink->parent == TC_H_ROOT)
err = nfp_abm_ctrl_read_stats(alink, &stats);
else
err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
if (err)
return err;
nfp_abm_update_stats(&stats, prev_stats, &opt->stats);
*prev_stats = stats;
return 0;
}
static int
nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
{
struct nfp_alink_xstats *prev_xstats;
struct nfp_alink_xstats xstats;
int i, err;
i = nfp_abm_red_find(alink, opt);
if (i < 0)
return i;
prev_xstats = &alink->qdiscs[i].xstats;
if (alink->parent == TC_H_ROOT)
err = nfp_abm_ctrl_read_xstats(alink, &xstats);
else
err = nfp_abm_ctrl_read_q_xstats(alink, i, &xstats);
if (err)
return err;
opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked;
opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop;
*prev_xstats = xstats;
return 0;
}
static int
nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
struct tc_red_qopt_offload *opt)
{
switch (opt->command) {
case TC_RED_REPLACE:
return nfp_abm_red_replace(netdev, alink, opt);
case TC_RED_DESTROY:
nfp_abm_red_destroy(netdev, alink, opt->handle);
return 0;
case TC_RED_STATS:
return nfp_abm_red_stats(alink, opt);
case TC_RED_XSTATS:
return nfp_abm_red_xstats(alink, opt);
default:
return -EOPNOTSUPP;
}
}
static int
nfp_abm_mq_stats(struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt)
{
struct nfp_alink_stats stats;
unsigned int i;
int err;
for (i = 0; i < alink->num_qdiscs; i++) {
if (alink->qdiscs[i].handle == TC_H_UNSPEC)
continue;
err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
if (err)
return err;
nfp_abm_update_stats(&stats, &alink->qdiscs[i].stats,
&opt->stats);
}
return 0;
}
static int
nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
struct tc_mq_qopt_offload *opt)
{
switch (opt->command) {
case TC_MQ_CREATE:
nfp_abm_reset_root(netdev, alink, opt->handle,
alink->total_queues);
return 0;
case TC_MQ_DESTROY:
if (opt->handle == alink->parent)
nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
return 0;
case TC_MQ_STATS:
return nfp_abm_mq_stats(alink, opt);
default:
return -EOPNOTSUPP;
}
}
static int
nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev,
enum tc_setup_type type, void *type_data)
{
struct nfp_repr *repr = netdev_priv(netdev);
struct nfp_port *port;
port = nfp_port_from_netdev(netdev);
if (!port || port->type != NFP_PORT_PF_PORT)
return -EOPNOTSUPP;
switch (type) {
case TC_SETUP_QDISC_MQ:
return nfp_abm_setup_tc_mq(netdev, repr->app_priv, type_data);
case TC_SETUP_QDISC_RED:
return nfp_abm_setup_tc_red(netdev, repr->app_priv, type_data);
default:
return -EOPNOTSUPP;
}
}
static struct net_device *nfp_abm_repr_get(struct nfp_app *app, u32 port_id)
{
enum nfp_repr_type rtype;
......@@ -83,14 +370,18 @@ nfp_abm_spawn_repr(struct nfp_app *app, struct nfp_abm_link *alink,
struct nfp_reprs *reprs;
struct nfp_repr *repr;
struct nfp_port *port;
unsigned int txqs;
int err;
if (ptype == NFP_PORT_PHYS_PORT)
if (ptype == NFP_PORT_PHYS_PORT) {
rtype = NFP_REPR_TYPE_PHYS_PORT;
else
txqs = 1;
} else {
rtype = NFP_REPR_TYPE_PF;
txqs = alink->vnic->max_rx_rings;
}
netdev = nfp_repr_alloc(app);
netdev = nfp_repr_alloc_mqs(app, txqs, 1);
if (!netdev)
return -ENOMEM;
repr = netdev_priv(netdev);
......@@ -182,6 +473,7 @@ static enum devlink_eswitch_mode nfp_abm_eswitch_mode_get(struct nfp_app *app)
static int nfp_abm_eswitch_set_legacy(struct nfp_abm *abm)
{
nfp_abm_kill_reprs_all(abm);
nfp_abm_ctrl_qm_disable(abm);
abm->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY;
return 0;
......@@ -200,6 +492,10 @@ static int nfp_abm_eswitch_set_switchdev(struct nfp_abm *abm)
struct nfp_net *nn;
int err;
err = nfp_abm_ctrl_qm_enable(abm);
if (err)
return err;
list_for_each_entry(nn, &pf->vnics, vnic_list) {
struct nfp_abm_link *alink = nn->app_priv;
......@@ -217,6 +513,7 @@ static int nfp_abm_eswitch_set_switchdev(struct nfp_abm *abm)
err_kill_all_reprs:
nfp_abm_kill_reprs_all(abm);
nfp_abm_ctrl_qm_disable(abm);
return err;
}
......@@ -291,13 +588,21 @@ nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
alink->abm = abm;
alink->vnic = nn;
alink->id = id;
alink->parent = TC_H_ROOT;
alink->total_queues = alink->vnic->max_rx_rings;
alink->qdiscs = kvzalloc(sizeof(*alink->qdiscs) * alink->total_queues,
GFP_KERNEL);
if (!alink->qdiscs) {
err = -ENOMEM;
goto err_free_alink;
}
/* This is a multi-host app, make sure MAC/PHY is up, but don't
* make the MAC/PHY state follow the state of any of the ports.
*/
err = nfp_eth_set_configured(app->cpp, eth_port->index, true);
if (err < 0)
goto err_free_alink;
goto err_free_qdiscs;
netif_keep_dst(nn->dp.netdev);
......@@ -306,6 +611,8 @@ nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id)
return 0;
err_free_qdiscs:
kvfree(alink->qdiscs);
err_free_alink:
kfree(alink);
return err;
......@@ -316,9 +623,57 @@ static void nfp_abm_vnic_free(struct nfp_app *app, struct nfp_net *nn)
struct nfp_abm_link *alink = nn->app_priv;
nfp_abm_kill_reprs(alink->abm, alink);
kvfree(alink->qdiscs);
kfree(alink);
}
static u64 *
nfp_abm_port_get_stats(struct nfp_app *app, struct nfp_port *port, u64 *data)
{
struct nfp_repr *repr = netdev_priv(port->netdev);
struct nfp_abm_link *alink;
unsigned int i;
if (port->type != NFP_PORT_PF_PORT)
return data;
alink = repr->app_priv;
for (i = 0; i < alink->vnic->dp.num_r_vecs; i++) {
*data++ = nfp_abm_ctrl_stat_non_sto(alink, i);
*data++ = nfp_abm_ctrl_stat_sto(alink, i);
}
return data;
}
static int
nfp_abm_port_get_stats_count(struct nfp_app *app, struct nfp_port *port)
{
struct nfp_repr *repr = netdev_priv(port->netdev);
struct nfp_abm_link *alink;
if (port->type != NFP_PORT_PF_PORT)
return 0;
alink = repr->app_priv;
return alink->vnic->dp.num_r_vecs * 2;
}
static u8 *
nfp_abm_port_get_stats_strings(struct nfp_app *app, struct nfp_port *port,
u8 *data)
{
struct nfp_repr *repr = netdev_priv(port->netdev);
struct nfp_abm_link *alink;
unsigned int i;
if (port->type != NFP_PORT_PF_PORT)
return data;
alink = repr->app_priv;
for (i = 0; i < alink->vnic->dp.num_r_vecs; i++) {
data = nfp_pr_et(data, "q%u_no_wait", i);
data = nfp_pr_et(data, "q%u_delayed", i);
}
return data;
}
static int nfp_abm_init(struct nfp_app *app)
{
struct nfp_pf *pf = app->pf;
......@@ -350,6 +705,11 @@ static int nfp_abm_init(struct nfp_app *app)
if (err)
goto err_free_abm;
/* We start in legacy mode, make sure advanced queuing is disabled */
err = nfp_abm_ctrl_qm_disable(abm);
if (err)
goto err_free_abm;
err = -ENOMEM;
reprs = nfp_reprs_alloc(pf->max_data_vnics);
if (!reprs)
......@@ -392,6 +752,12 @@ const struct nfp_app_type app_abm = {
.vnic_alloc = nfp_abm_vnic_alloc,
.vnic_free = nfp_abm_vnic_free,
.port_get_stats = nfp_abm_port_get_stats,
.port_get_stats_count = nfp_abm_port_get_stats_count,
.port_get_stats_strings = nfp_abm_port_get_stats_strings,
.setup_tc = nfp_abm_setup_tc,
.eswitch_mode_get = nfp_abm_eswitch_mode_get,
.eswitch_mode_set = nfp_abm_eswitch_mode_set,
......
......@@ -49,11 +49,55 @@ struct nfp_net;
* @pf_id: ID of our PF link
* @eswitch_mode: devlink eswitch mode, advanced functions only visible
* in switchdev mode
* @q_lvls: queue level control area
* @qm_stats: queue statistics symbol
*/
struct nfp_abm {
struct nfp_app *app;
unsigned int pf_id;
enum devlink_eswitch_mode eswitch_mode;
const struct nfp_rtsym *q_lvls;
const struct nfp_rtsym *qm_stats;
};
/**
* struct nfp_alink_stats - ABM NIC statistics
* @tx_pkts: number of TXed packets
* @tx_bytes: number of TXed bytes
* @backlog_pkts: momentary backlog length (packets)
* @backlog_bytes: momentary backlog length (bytes)
* @overlimits: number of ECN marked TXed packets (accumulative)
* @drops: number of tail-dropped packets (accumulative)
*/
struct nfp_alink_stats {
u64 tx_pkts;
u64 tx_bytes;
u64 backlog_pkts;
u64 backlog_bytes;
u64 overlimits;
u64 drops;
};
/**
* struct nfp_alink_xstats - extended ABM NIC statistics
* @ecn_marked: number of ECN marked TXed packets
* @pdrop: number of hard drops due to queue limit
*/
struct nfp_alink_xstats {
u64 ecn_marked;
u64 pdrop;
};
/**
* struct nfp_red_qdisc - representation of single RED Qdisc
* @handle: handle of currently offloaded RED Qdisc
* @stats: statistics from last refresh
* @xstats: base of extended statistics
*/
struct nfp_red_qdisc {
u32 handle;
struct nfp_alink_stats stats;
struct nfp_alink_xstats xstats;
};
/**
......@@ -62,14 +106,37 @@ struct nfp_abm {
* @vnic: data vNIC
* @id: id of the data vNIC
* @queue_base: id of base to host queue within PCIe (not QC idx)
* @total_queues: number of PF queues
* @parent: handle of expected parent, i.e. handle of MQ, or TC_H_ROOT
* @num_qdiscs: number of currently used qdiscs
* @qdiscs: array of qdiscs
*/
struct nfp_abm_link {
struct nfp_abm *abm;
struct nfp_net *vnic;
unsigned int id;
unsigned int queue_base;
unsigned int total_queues;
u32 parent;
unsigned int num_qdiscs;
struct nfp_red_qdisc *qdiscs;
};
void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val);
int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int i,
u32 val);
int nfp_abm_ctrl_read_stats(struct nfp_abm_link *alink,
struct nfp_alink_stats *stats);
int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink, unsigned int i,
struct nfp_alink_stats *stats);
int nfp_abm_ctrl_read_xstats(struct nfp_abm_link *alink,
struct nfp_alink_xstats *xstats);
int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink, unsigned int i,
struct nfp_alink_xstats *xstats);
u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i);
u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i);
int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm);
int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm);
#endif
......@@ -59,12 +59,26 @@
* @NFP_MBOX_POOL_SET: set shared buffer pool info/config
* Input - struct nfp_shared_buf_pool_info_set
* Output - None
*
* @NFP_MBOX_PCIE_ABM_ENABLE: enable PCIe-side advanced buffer management
* Enable advanced buffer management of the PCIe block. If ABM is disabled
* PCIe block maintains a very short queue of buffers and does tail drop.
* ABM allows more advanced buffering and priority control.
* Input - None
* Output - None
*
* @NFP_MBOX_PCIE_ABM_DISABLE: disable PCIe-side advanced buffer management
* Input - None
* Output - None
*/
enum nfp_mbox_cmd {
NFP_MBOX_NO_CMD = 0x00,
NFP_MBOX_POOL_GET = 0x01,
NFP_MBOX_POOL_SET = 0x02,
NFP_MBOX_PCIE_ABM_ENABLE = 0x03,
NFP_MBOX_PCIE_ABM_DISABLE = 0x04,
};
#define NFP_SHARED_BUF_COUNT_SYM_NAME "_abi_nfd_pf%u_sb_cnt"
......
......@@ -43,6 +43,7 @@
#include "nfp_main.h"
#include "nfp_net.h"
#include "nfp_net_repr.h"
#include "nfp_port.h"
static const struct nfp_app_type *apps[] = {
[NFP_APP_CORE_NIC] = &app_nic,
......@@ -85,6 +86,27 @@ const char *nfp_app_mip_name(struct nfp_app *app)
return nfp_mip_name(app->pf->mip);
}
u64 *nfp_app_port_get_stats(struct nfp_port *port, u64 *data)
{
if (!port || !port->app || !port->app->type->port_get_stats)
return data;
return port->app->type->port_get_stats(port->app, port, data);
}
int nfp_app_port_get_stats_count(struct nfp_port *port)
{
if (!port || !port->app || !port->app->type->port_get_stats_count)
return 0;
return port->app->type->port_get_stats_count(port->app, port);
}
u8 *nfp_app_port_get_stats_strings(struct nfp_port *port, u8 *data)
{
if (!port || !port->app || !port->app->type->port_get_stats_strings)
return data;
return port->app->type->port_get_stats_strings(port->app, port, data);
}
struct sk_buff *
nfp_app_ctrl_msg_alloc(struct nfp_app *app, unsigned int size, gfp_t priority)
{
......
......@@ -90,6 +90,9 @@ extern const struct nfp_app_type app_abm;
* @repr_stop: representor netdev stop callback
* @check_mtu: MTU change request on a netdev (verify it is valid)
* @repr_change_mtu: MTU change request on repr (make and verify change)
* @port_get_stats: get extra ethtool statistics for a port
* @port_get_stats_count: get count of extra statistics for a port
* @port_get_stats_strings: get strings for extra statistics
* @start: start application logic
* @stop: stop application logic
* @ctrl_msg_rx: control message handler
......@@ -132,6 +135,12 @@ struct nfp_app_type {
int (*repr_change_mtu)(struct nfp_app *app, struct net_device *netdev,
int new_mtu);
u64 *(*port_get_stats)(struct nfp_app *app,
struct nfp_port *port, u64 *data);
int (*port_get_stats_count)(struct nfp_app *app, struct nfp_port *port);
u8 *(*port_get_stats_strings)(struct nfp_app *app,
struct nfp_port *port, u8 *data);
int (*start)(struct nfp_app *app);
void (*stop)(struct nfp_app *app);
......@@ -404,6 +413,10 @@ static inline struct net_device *nfp_app_repr_get(struct nfp_app *app, u32 id)
struct nfp_app *nfp_app_from_netdev(struct net_device *netdev);
u64 *nfp_app_port_get_stats(struct nfp_port *port, u64 *data);
int nfp_app_port_get_stats_count(struct nfp_port *port);
u8 *nfp_app_port_get_stats_strings(struct nfp_port *port, u8 *data);
struct nfp_reprs *
nfp_reprs_get_locked(struct nfp_app *app, enum nfp_repr_type type);
struct nfp_reprs *
......
......@@ -3286,11 +3286,12 @@ nfp_net_get_phys_port_name(struct net_device *netdev, char *name, size_t len)
if (nn->port)
return nfp_port_get_phys_port_name(netdev, name, len);
if (!nn->dp.is_vf) {
n = snprintf(name, len, "%d", nn->id);
if (n >= len)
return -EINVAL;
}
if (nn->dp.is_vf)
return -EOPNOTSUPP;
n = snprintf(name, len, "n%d", nn->id);
if (n >= len)
return -EINVAL;
return 0;
}
......
......@@ -437,7 +437,7 @@ static int nfp_net_set_ringparam(struct net_device *netdev,
return nfp_net_set_ring_size(nn, rxd_cnt, txd_cnt);
}
static __printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...)
__printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...)
{
va_list args;
......@@ -637,6 +637,7 @@ static void nfp_net_get_strings(struct net_device *netdev,
nn->dp.num_tx_rings,
false);
data = nfp_mac_get_stats_strings(netdev, data);
data = nfp_app_port_get_stats_strings(nn->port, data);
break;
}
}
......@@ -651,6 +652,7 @@ nfp_net_get_stats(struct net_device *netdev, struct ethtool_stats *stats,
data = nfp_vnic_get_hw_stats(data, nn->dp.ctrl_bar,
nn->dp.num_rx_rings, nn->dp.num_tx_rings);
data = nfp_mac_get_stats(netdev, data);
data = nfp_app_port_get_stats(nn->port, data);
}
static int nfp_net_get_sset_count(struct net_device *netdev, int sset)
......@@ -662,7 +664,8 @@ static int nfp_net_get_sset_count(struct net_device *netdev, int sset)
return nfp_vnic_get_sw_stats_count(netdev) +
nfp_vnic_get_hw_stats_count(nn->dp.num_rx_rings,
nn->dp.num_tx_rings) +
nfp_mac_get_stats_count(netdev);
nfp_mac_get_stats_count(netdev) +
nfp_app_port_get_stats_count(nn->port);
default:
return -EOPNOTSUPP;
}
......@@ -679,6 +682,7 @@ static void nfp_port_get_strings(struct net_device *netdev,
data = nfp_vnic_get_hw_stats_strings(data, 0, 0, true);
else
data = nfp_mac_get_stats_strings(netdev, data);
data = nfp_app_port_get_stats_strings(port, data);
break;
}
}
......@@ -693,6 +697,7 @@ nfp_port_get_stats(struct net_device *netdev, struct ethtool_stats *stats,
data = nfp_vnic_get_hw_stats(data, port->vnic, 0, 0);
else
data = nfp_mac_get_stats(netdev, data);
data = nfp_app_port_get_stats(port, data);
}
static int nfp_port_get_sset_count(struct net_device *netdev, int sset)
......@@ -706,6 +711,7 @@ static int nfp_port_get_sset_count(struct net_device *netdev, int sset)
count = nfp_vnic_get_hw_stats_count(0, 0);
else
count = nfp_mac_get_stats_count(netdev);
count += nfp_app_port_get_stats_count(port);
return count;
default:
return -EOPNOTSUPP;
......
......@@ -360,12 +360,13 @@ void nfp_repr_free(struct net_device *netdev)
__nfp_repr_free(netdev_priv(netdev));
}
struct net_device *nfp_repr_alloc(struct nfp_app *app)
struct net_device *
nfp_repr_alloc_mqs(struct nfp_app *app, unsigned int txqs, unsigned int rxqs)
{
struct net_device *netdev;
struct nfp_repr *repr;
netdev = alloc_etherdev(sizeof(*repr));
netdev = alloc_etherdev_mqs(sizeof(*repr), txqs, rxqs);
if (!netdev)
return NULL;
......
......@@ -126,7 +126,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
u32 cmsg_port_id, struct nfp_port *port,
struct net_device *pf_netdev);
void nfp_repr_free(struct net_device *netdev);
struct net_device *nfp_repr_alloc(struct nfp_app *app);
struct net_device *
nfp_repr_alloc_mqs(struct nfp_app *app, unsigned int txqs, unsigned int rxqs);
void nfp_repr_clean_and_free(struct nfp_repr *repr);
void nfp_reprs_clean_and_free(struct nfp_app *app, struct nfp_reprs *reprs);
void nfp_reprs_clean_and_free_by_type(struct nfp_app *app,
......@@ -134,4 +135,8 @@ void nfp_reprs_clean_and_free_by_type(struct nfp_app *app,
struct nfp_reprs *nfp_reprs_alloc(unsigned int num_reprs);
int nfp_reprs_resync_phys_ports(struct nfp_app *app);
static inline struct net_device *nfp_repr_alloc(struct nfp_app *app)
{
return nfp_repr_alloc_mqs(app, 1, 1);
}
#endif /* NFP_NET_REPR_H */
......@@ -122,6 +122,8 @@ struct nfp_port {
extern const struct ethtool_ops nfp_port_ethtool_ops;
extern const struct switchdev_ops nfp_port_switchdev_ops;
__printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...);
int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type,
void *type_data);
......
......@@ -87,6 +87,11 @@ struct resource;
#define NFP_CPP_TARGET_ID_MASK 0x1f
#define NFP_CPP_ATOMIC_RD(target, island) \
NFP_CPP_ISLAND_ID((target), 3, 0, (island))
#define NFP_CPP_ATOMIC_WR(target, island) \
NFP_CPP_ISLAND_ID((target), 4, 0, (island))
/**
* NFP_CPP_ID() - pack target, token, and action into a CPP ID.
* @target: NFP CPP target id
......
......@@ -791,6 +791,7 @@ enum tc_setup_type {
TC_SETUP_QDISC_CBS,
TC_SETUP_QDISC_RED,
TC_SETUP_QDISC_PRIO,
TC_SETUP_QDISC_MQ,
};
/* These structures hold the attributes of bpf state that are being passed
......
......@@ -778,6 +778,18 @@ struct tc_qopt_offload_stats {
struct gnet_stats_queue *qstats;
};
enum tc_mq_command {
TC_MQ_CREATE,
TC_MQ_DESTROY,
TC_MQ_STATS,
};
struct tc_mq_qopt_offload {
enum tc_mq_command command;
u32 handle;
struct tc_qopt_offload_stats stats;
};
enum tc_red_command {
TC_RED_REPLACE,
TC_RED_DESTROY,
......
......@@ -350,14 +350,14 @@ static inline int qdisc_qlen(const struct Qdisc *q)
static inline int qdisc_qlen_sum(const struct Qdisc *q)
{
__u32 qlen = 0;
__u32 qlen = q->qstats.qlen;
int i;
if (q->flags & TCQ_F_NOLOCK) {
for_each_possible_cpu(i)
qlen += per_cpu_ptr(q->cpu_qstats, i)->qlen;
} else {
qlen = q->q.qlen;
qlen += q->q.qlen;
}
return qlen;
......
......@@ -16,6 +16,7 @@
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <net/netlink.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>
#include <net/sch_generic.h>
......@@ -23,12 +24,44 @@ struct mq_sched {
struct Qdisc **qdiscs;
};
static int mq_offload(struct Qdisc *sch, enum tc_mq_command cmd)
{
struct net_device *dev = qdisc_dev(sch);
struct tc_mq_qopt_offload opt = {
.command = cmd,
.handle = sch->handle,
};
if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
return -EOPNOTSUPP;
return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQ, &opt);
}
static void mq_offload_stats(struct Qdisc *sch)
{
struct net_device *dev = qdisc_dev(sch);
struct tc_mq_qopt_offload opt = {
.command = TC_MQ_STATS,
.handle = sch->handle,
.stats = {
.bstats = &sch->bstats,
.qstats = &sch->qstats,
},
};
if (tc_can_offload(dev) && dev->netdev_ops->ndo_setup_tc)
dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_MQ, &opt);
}
static void mq_destroy(struct Qdisc *sch)
{
struct net_device *dev = qdisc_dev(sch);
struct mq_sched *priv = qdisc_priv(sch);
unsigned int ntx;
mq_offload(sch, TC_MQ_DESTROY);
if (!priv->qdiscs)
return;
for (ntx = 0; ntx < dev->num_tx_queues && priv->qdiscs[ntx]; ntx++)
......@@ -70,6 +103,8 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt,
}
sch->flags |= TCQ_F_MQROOT;
mq_offload(sch, TC_MQ_CREATE);
return 0;
}
......@@ -127,6 +162,7 @@ static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
sch->q.qlen += qdisc->q.qlen;
sch->bstats.bytes += qdisc->bstats.bytes;
sch->bstats.packets += qdisc->bstats.packets;
sch->qstats.qlen += qdisc->qstats.qlen;
sch->qstats.backlog += qdisc->qstats.backlog;
sch->qstats.drops += qdisc->qstats.drops;
sch->qstats.requeues += qdisc->qstats.requeues;
......@@ -135,6 +171,7 @@ static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
spin_unlock_bh(qdisc_lock(qdisc));
}
mq_offload_stats(sch);
return 0;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册