void *vaddr;
struct sk_buff *skb;
struct rtnl_link_stats64 *percpu_stats;
+ struct dpaa2_eth_drv_stats *percpu_extras;
struct device *dev = priv->net_dev->dev.parent;
struct dpaa2_fas *fas;
u32 status = 0;
prefetch(vaddr + dpaa2_fd_get_offset(fd));
percpu_stats = this_cpu_ptr(priv->percpu_stats);
+ percpu_extras = this_cpu_ptr(priv->percpu_extras);
if (fd_format == dpaa2_fd_single) {
skb = build_linear_skb(priv, ch, fd, vaddr);
vaddr + dpaa2_fd_get_offset(fd);
skb = build_frag_skb(priv, ch, sgt);
skb_free_frag(vaddr);
+ percpu_extras->rx_sg_frames++;
+ percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd);
} else {
/* We don't support any other format */
goto err_frame_format;
fd = dpaa2_dq_fd(dq);
fq = (struct dpaa2_eth_fq *)dpaa2_dq_fqd_ctx(dq);
+ fq->stats.frames++;
fq->consume(priv, ch, fd, &ch->napi);
cleaned++;
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
struct dpaa2_fd fd;
struct rtnl_link_stats64 *percpu_stats;
+ struct dpaa2_eth_drv_stats *percpu_extras;
struct dpaa2_eth_fq *fq;
u16 queue_mapping;
int err, i;
percpu_stats = this_cpu_ptr(priv->percpu_stats);
+ percpu_extras = this_cpu_ptr(priv->percpu_extras);
if (unlikely(skb_headroom(skb) < DPAA2_ETH_NEEDED_HEADROOM(priv))) {
struct sk_buff *ns;
/* Setup the FD fields */
memset(&fd, 0, sizeof(fd));
- if (skb_is_nonlinear(skb))
+ if (skb_is_nonlinear(skb)) {
err = build_sg_fd(priv, skb, &fd);
- else
+ percpu_extras->tx_sg_frames++;
+ percpu_extras->tx_sg_bytes += skb->len;
+ } else {
err = build_single_fd(priv, skb, &fd);
+ }
+
if (unlikely(err)) {
percpu_stats->tx_dropped++;
goto err_build_fd;
if (err != -EBUSY)
break;
}
+ percpu_extras->tx_portal_busy += i;
if (unlikely(err < 0)) {
percpu_stats->tx_errors++;
/* Clean up everything, including freeing the skb */
struct napi_struct *napi __always_unused)
{
struct rtnl_link_stats64 *percpu_stats;
+ struct dpaa2_eth_drv_stats *percpu_extras;
u32 status = 0;
+ percpu_extras = this_cpu_ptr(priv->percpu_extras);
+ percpu_extras->tx_conf_frames++;
+ percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd);
+
free_tx_fd(priv, fd, &status);
if (unlikely(status & DPAA2_ETH_TXCONF_ERR_MASK)) {
static int pull_channel(struct dpaa2_eth_channel *ch)
{
int err;
+ int dequeues = -1;
/* Retry while portal is busy */
do {
err = dpaa2_io_service_pull_channel(NULL, ch->ch_id, ch->store);
+ dequeues++;
cpu_relax();
} while (err == -EBUSY);
+ ch->stats.dequeue_portal_busy += dequeues;
+ if (unlikely(err))
+ ch->stats.pull_err++;
+
return err;
}
} while (err == -EBUSY);
}
+ ch->stats.frames += cleaned;
+
return cleaned;
}
struct dpaa2_eth_channel *ch;
ch = container_of(ctx, struct dpaa2_eth_channel, nctx);
+
+ /* Update NAPI statistics */
+ ch->stats.cdan++;
+
napi_schedule_irqoff(&ch->napi);
}
err = -ENOMEM;
goto err_alloc_percpu_stats;
}
+ priv->percpu_extras = alloc_percpu(*priv->percpu_extras);
+ if (!priv->percpu_extras) {
+ dev_err(dev, "alloc_percpu(percpu_extras) failed\n");
+ err = -ENOMEM;
+ goto err_alloc_percpu_extras;
+ }
err = netdev_init(net_dev);
if (err)
err_csum:
unregister_netdev(net_dev);
err_netdev_init:
+ free_percpu(priv->percpu_extras);
+err_alloc_percpu_extras:
free_percpu(priv->percpu_stats);
err_alloc_percpu_stats:
del_ch_napi(priv);
free_rings(priv);
free_percpu(priv->percpu_stats);
+ free_percpu(priv->percpu_extras);
del_ch_napi(priv);
free_dpbp(priv);
*/
#define DPAA2_ETH_ENQUEUE_RETRIES 10
+/* Driver statistics, other than those in struct rtnl_link_stats64.
+ * These are usually collected per-CPU and aggregated by ethtool.
+ */
+struct dpaa2_eth_drv_stats {
+ __u64 tx_conf_frames;
+ __u64 tx_conf_bytes;
+ __u64 tx_sg_frames;
+ __u64 tx_sg_bytes;
+ __u64 rx_sg_frames;
+ __u64 rx_sg_bytes;
+ /* Enqueues retried due to portal busy */
+ __u64 tx_portal_busy;
+};
+
+/* Per-FQ statistics */
+struct dpaa2_eth_fq_stats {
+ /* Number of frames received on this queue */
+ __u64 frames;
+};
+
+/* Per-channel statistics */
+struct dpaa2_eth_ch_stats {
+ /* Volatile dequeues retried due to portal busy */
+ __u64 dequeue_portal_busy;
+ /* Number of CDANs; useful to estimate avg NAPI len */
+ __u64 cdan;
+ /* Number of frames received on queues from this channel */
+ __u64 frames;
+ /* Pull errors */
+ __u64 pull_err;
+};
+
/* Maximum number of queues associated with a DPNI */
#define DPAA2_ETH_MAX_RX_QUEUES 16
#define DPAA2_ETH_MAX_TX_QUEUES NR_CPUS
struct dpaa2_eth_channel *,
const struct dpaa2_fd *,
struct napi_struct *);
+ struct dpaa2_eth_fq_stats stats;
};
struct dpaa2_eth_channel {
struct dpaa2_io_store *store;
struct dpaa2_eth_priv *priv;
int buf_count;
+ struct dpaa2_eth_ch_stats stats;
};
struct dpaa2_eth_hash_fields {
/* Standard statistics */
struct rtnl_link_stats64 __percpu *percpu_stats;
+ /* Extra stats, in addition to the ones known by the kernel */
+ struct dpaa2_eth_drv_stats __percpu *percpu_extras;
u16 mc_token;
#define DPAA2_ETH_NUM_STATS ARRAY_SIZE(dpaa2_ethtool_stats)
+char dpaa2_ethtool_extras[][ETH_GSTRING_LEN] = {
+ /* per-cpu stats */
+ "tx conf frames",
+ "tx conf bytes",
+ "tx sg frames",
+ "tx sg bytes",
+ "rx sg frames",
+ "rx sg bytes",
+ "enqueue portal busy",
+ /* Channel stats */
+ "dequeue portal busy",
+ "channel pull errors",
+ "cdan",
+};
+
+#define DPAA2_ETH_NUM_EXTRA_STATS ARRAY_SIZE(dpaa2_ethtool_extras)
+
static void dpaa2_eth_get_drvinfo(struct net_device *net_dev,
struct ethtool_drvinfo *drvinfo)
{
strlcpy(p, dpaa2_ethtool_stats[i], ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
+ for (i = 0; i < DPAA2_ETH_NUM_EXTRA_STATS; i++) {
+ strlcpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
break;
}
}
{
switch (sset) {
case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */
- return DPAA2_ETH_NUM_STATS;
+ return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS;
default:
return -EOPNOTSUPP;
}
int j, k, err;
int num_cnt;
union dpni_statistics dpni_stats;
+ u64 cdan = 0;
+ u64 portal_busy = 0, pull_err = 0;
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ struct dpaa2_eth_drv_stats *extras;
+ struct dpaa2_eth_ch_stats *ch_stats;
- memset(data, 0, sizeof(u64) * DPAA2_ETH_NUM_STATS);
+ memset(data, 0,
+ sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS));
/* Print standard counters, from DPNI statistics */
for (j = 0; j <= 2; j++) {
for (k = 0; k < num_cnt; k++)
*(data + i++) = dpni_stats.raw.counter[k];
}
+
+ /* Print per-cpu extra stats */
+ for_each_online_cpu(k) {
+ extras = per_cpu_ptr(priv->percpu_extras, k);
+ for (j = 0; j < sizeof(*extras) / sizeof(__u64); j++)
+ *((__u64 *)data + i + j) += *((__u64 *)extras + j);
+ }
+ i += j;
+
+ for (j = 0; j < priv->num_channels; j++) {
+ ch_stats = &priv->channel[j]->stats;
+ cdan += ch_stats->cdan;
+ portal_busy += ch_stats->dequeue_portal_busy;
+ pull_err += ch_stats->pull_err;
+ }
+
+ *(data + i++) = portal_busy;
+ *(data + i++) = pull_err;
+ *(data + i++) = cdan;
}
static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,