virtio-net: support queue stat

To enhance functionality, we now support reporting statistics through
the netdev-generic netlink (netdev-genl) queue stats interface. However,
this does not extend to all statistics, so a new field, qstat_offset,
has been introduced. This field determines which statistics should be
reported via netdev-genl queue stats.

Given that queue stats are retrieved individually per queue, it's
necessary for the virtnet_get_hw_stats() function to be capable of
fetching statistics for a specific queue.

As the document https://docs.kernel.org/next/networking/statistics.html#notes-for-driver-authors

We should not duplicate the stats which get reported via the netlink API in
ethtool. If the stats are for queue stat, that will not be reported by
ethtool -S.

python3 ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml
    --dump qstats-get --json '{"scope": "queue"}'
[{'ifindex': 2,
  'queue-id': 0,
  'queue-type': 'rx',
  'rx-bytes': 157844011,
  'rx-csum-bad': 0,
  'rx-csum-none': 0,
  'rx-csum-unnecessary': 2195386,
  'rx-hw-drop-overruns': 0,
  'rx-hw-drop-ratelimits': 0,
  'rx-hw-drops': 12964,
  'rx-packets': 598929},
 {'ifindex': 2,
  'queue-id': 0,
  'queue-type': 'tx',
  'tx-bytes': 1938511,
  'tx-csum-none': 0,
  'tx-hw-drop-errors': 0,
  'tx-hw-drop-ratelimits': 0,
  'tx-hw-drops': 0,
  'tx-needs-csum': 61263,
  'tx-packets': 15515}]

Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Reviewed-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Xuan Zhuo 2024-04-26 11:39:28 +08:00 committed by Paolo Abeni
parent 0cfe71f45f
commit d888f04c09

View File

@ -24,6 +24,7 @@
#include <net/xdp.h>
#include <net/net_failover.h>
#include <net/netdev_rx_queue.h>
#include <net/netdev_queues.h>
static int napi_weight = NAPI_POLL_WEIGHT;
module_param(napi_weight, int, 0444);
@ -78,6 +79,7 @@ static const unsigned long guest_offloads[] = {
struct virtnet_stat_desc {
char desc[ETH_GSTRING_LEN];
size_t offset;
size_t qstat_offset;
};
struct virtnet_sq_free_stats {
@ -107,12 +109,24 @@ struct virtnet_rq_stats {
u64_stats_t kicks;
};
#define VIRTNET_SQ_STAT(name, m) {name, offsetof(struct virtnet_sq_stats, m)}
#define VIRTNET_RQ_STAT(name, m) {name, offsetof(struct virtnet_rq_stats, m)}
#define VIRTNET_SQ_STAT(name, m) {name, offsetof(struct virtnet_sq_stats, m), -1}
#define VIRTNET_RQ_STAT(name, m) {name, offsetof(struct virtnet_rq_stats, m), -1}
#define VIRTNET_SQ_STAT_QSTAT(name, m) \
{ \
name, \
offsetof(struct virtnet_sq_stats, m), \
offsetof(struct netdev_queue_stats_tx, m), \
}
#define VIRTNET_RQ_STAT_QSTAT(name, m) \
{ \
name, \
offsetof(struct virtnet_rq_stats, m), \
offsetof(struct netdev_queue_stats_rx, m), \
}
static const struct virtnet_stat_desc virtnet_sq_stats_desc[] = {
VIRTNET_SQ_STAT("packets", packets),
VIRTNET_SQ_STAT("bytes", bytes),
VIRTNET_SQ_STAT("xdp_tx", xdp_tx),
VIRTNET_SQ_STAT("xdp_tx_drops", xdp_tx_drops),
VIRTNET_SQ_STAT("kicks", kicks),
@ -120,8 +134,6 @@ static const struct virtnet_stat_desc virtnet_sq_stats_desc[] = {
};
static const struct virtnet_stat_desc virtnet_rq_stats_desc[] = {
VIRTNET_RQ_STAT("packets", packets),
VIRTNET_RQ_STAT("bytes", bytes),
VIRTNET_RQ_STAT("drops", drops),
VIRTNET_RQ_STAT("xdp_packets", xdp_packets),
VIRTNET_RQ_STAT("xdp_tx", xdp_tx),
@ -130,14 +142,25 @@ static const struct virtnet_stat_desc virtnet_rq_stats_desc[] = {
VIRTNET_RQ_STAT("kicks", kicks),
};
static const struct virtnet_stat_desc virtnet_sq_stats_desc_qstat[] = {
VIRTNET_SQ_STAT_QSTAT("packets", packets),
VIRTNET_SQ_STAT_QSTAT("bytes", bytes),
};
static const struct virtnet_stat_desc virtnet_rq_stats_desc_qstat[] = {
VIRTNET_RQ_STAT_QSTAT("packets", packets),
VIRTNET_RQ_STAT_QSTAT("bytes", bytes),
};
#define VIRTNET_STATS_DESC_CQ(name) \
{#name, offsetof(struct virtio_net_stats_cvq, name)}
{#name, offsetof(struct virtio_net_stats_cvq, name), -1}
#define VIRTNET_STATS_DESC_RX(class, name) \
{#name, offsetof(struct virtio_net_stats_rx_ ## class, rx_ ## name)}
{#name, offsetof(struct virtio_net_stats_rx_ ## class, rx_ ## name), -1}
#define VIRTNET_STATS_DESC_TX(class, name) \
{#name, offsetof(struct virtio_net_stats_tx_ ## class, tx_ ## name)}
{#name, offsetof(struct virtio_net_stats_tx_ ## class, tx_ ## name), -1}
static const struct virtnet_stat_desc virtnet_stats_cvq_desc[] = {
VIRTNET_STATS_DESC_CQ(command_num),
@ -177,6 +200,63 @@ static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc[] = {
VIRTNET_STATS_DESC_TX(speed, ratelimit_bytes),
};
#define VIRTNET_STATS_DESC_RX_QSTAT(class, name, qstat_field) \
{ \
#name, \
offsetof(struct virtio_net_stats_rx_ ## class, rx_ ## name), \
offsetof(struct netdev_queue_stats_rx, qstat_field), \
}
#define VIRTNET_STATS_DESC_TX_QSTAT(class, name, qstat_field) \
{ \
#name, \
offsetof(struct virtio_net_stats_tx_ ## class, tx_ ## name), \
offsetof(struct netdev_queue_stats_tx, qstat_field), \
}
static const struct virtnet_stat_desc virtnet_stats_rx_basic_desc_qstat[] = {
VIRTNET_STATS_DESC_RX_QSTAT(basic, drops, hw_drops),
VIRTNET_STATS_DESC_RX_QSTAT(basic, drop_overruns, hw_drop_overruns),
};
static const struct virtnet_stat_desc virtnet_stats_tx_basic_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(basic, drops, hw_drops),
VIRTNET_STATS_DESC_TX_QSTAT(basic, drop_malformed, hw_drop_errors),
};
static const struct virtnet_stat_desc virtnet_stats_rx_csum_desc_qstat[] = {
VIRTNET_STATS_DESC_RX_QSTAT(csum, csum_valid, csum_unnecessary),
VIRTNET_STATS_DESC_RX_QSTAT(csum, csum_none, csum_none),
VIRTNET_STATS_DESC_RX_QSTAT(csum, csum_bad, csum_bad),
};
static const struct virtnet_stat_desc virtnet_stats_tx_csum_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(csum, csum_none, csum_none),
VIRTNET_STATS_DESC_TX_QSTAT(csum, needs_csum, needs_csum),
};
static const struct virtnet_stat_desc virtnet_stats_rx_gso_desc_qstat[] = {
VIRTNET_STATS_DESC_RX_QSTAT(gso, gso_packets, hw_gro_packets),
VIRTNET_STATS_DESC_RX_QSTAT(gso, gso_bytes, hw_gro_bytes),
VIRTNET_STATS_DESC_RX_QSTAT(gso, gso_packets_coalesced, hw_gro_wire_packets),
VIRTNET_STATS_DESC_RX_QSTAT(gso, gso_bytes_coalesced, hw_gro_wire_bytes),
};
static const struct virtnet_stat_desc virtnet_stats_tx_gso_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(gso, gso_packets, hw_gso_packets),
VIRTNET_STATS_DESC_TX_QSTAT(gso, gso_bytes, hw_gso_bytes),
VIRTNET_STATS_DESC_TX_QSTAT(gso, gso_segments, hw_gso_wire_packets),
VIRTNET_STATS_DESC_TX_QSTAT(gso, gso_segments_bytes, hw_gso_wire_bytes),
};
static const struct virtnet_stat_desc virtnet_stats_rx_speed_desc_qstat[] = {
VIRTNET_STATS_DESC_RX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
};
static const struct virtnet_stat_desc virtnet_stats_tx_speed_desc_qstat[] = {
VIRTNET_STATS_DESC_TX_QSTAT(speed, ratelimit_packets, hw_drop_ratelimits),
};
#define VIRTNET_Q_TYPE_RX 0
#define VIRTNET_Q_TYPE_TX 1
#define VIRTNET_Q_TYPE_CQ 2
@ -2215,6 +2295,10 @@ static int virtnet_receive(struct receive_queue *rq, int budget,
src = (u64_stats_t *)((u8 *)&stats + offset);
u64_stats_add(item, u64_stats_read(src));
}
u64_stats_add(&rq->stats.packets, u64_stats_read(&stats.packets));
u64_stats_add(&rq->stats.bytes, u64_stats_read(&stats.bytes));
u64_stats_update_end(&rq->stats.syncp);
return packets;
@ -3474,6 +3558,9 @@ static void virtnet_get_stats_string(struct virtnet_info *vi, int type, int qid,
}
struct virtnet_stats_ctx {
/* The stats are write to qstats or ethtool -S */
bool to_qstat;
/* Used to calculate the offset inside the output buffer. */
u32 desc_num[3];
@ -3489,11 +3576,71 @@ struct virtnet_stats_ctx {
static void virtnet_stats_ctx_init(struct virtnet_info *vi,
struct virtnet_stats_ctx *ctx,
u64 *data)
u64 *data, bool to_qstat)
{
u32 queue_type;
ctx->data = data;
ctx->to_qstat = to_qstat;
if (to_qstat) {
ctx->desc_num[VIRTNET_Q_TYPE_RX] = ARRAY_SIZE(virtnet_rq_stats_desc_qstat);
ctx->desc_num[VIRTNET_Q_TYPE_TX] = ARRAY_SIZE(virtnet_sq_stats_desc_qstat);
queue_type = VIRTNET_Q_TYPE_RX;
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_BASIC) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_RX_BASIC;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_rx_basic_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_rx_basic);
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_CSUM) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_RX_CSUM;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_rx_csum_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_rx_csum);
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_GSO) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_RX_GSO;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_rx_gso_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_rx_gso);
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_SPEED) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_RX_SPEED;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_rx_speed_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_rx_speed);
}
queue_type = VIRTNET_Q_TYPE_TX;
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_BASIC) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_TX_BASIC;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_tx_basic_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_tx_basic);
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_CSUM) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_TX_CSUM;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_tx_csum_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_tx_csum);
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_GSO) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_TX_GSO;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_tx_gso_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_tx_gso);
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_SPEED) {
ctx->bitmap[queue_type] |= VIRTIO_NET_STATS_TYPE_TX_SPEED;
ctx->desc_num[queue_type] += ARRAY_SIZE(virtnet_stats_tx_speed_desc_qstat);
ctx->size[queue_type] += sizeof(struct virtio_net_stats_tx_speed);
}
return;
}
ctx->desc_num[VIRTNET_Q_TYPE_RX] = ARRAY_SIZE(virtnet_rq_stats_desc);
ctx->desc_num[VIRTNET_Q_TYPE_TX] = ARRAY_SIZE(virtnet_sq_stats_desc);
@ -3590,7 +3737,104 @@ static void virtnet_fill_total_fields(struct virtnet_info *vi,
stats_sum_queue(data, num_tx, first_tx_q, vi->curr_queue_pairs);
}
/* virtnet_fill_stats - copy the stats to ethtool -S
static void virtnet_fill_stats_qstat(struct virtnet_info *vi, u32 qid,
struct virtnet_stats_ctx *ctx,
const u8 *base, bool drv_stats, u8 reply_type)
{
const struct virtnet_stat_desc *desc;
const u64_stats_t *v_stat;
u64 offset, bitmap;
const __le64 *v;
u32 queue_type;
int i, num;
queue_type = vq_type(vi, qid);
bitmap = ctx->bitmap[queue_type];
if (drv_stats) {
if (queue_type == VIRTNET_Q_TYPE_RX) {
desc = &virtnet_rq_stats_desc_qstat[0];
num = ARRAY_SIZE(virtnet_rq_stats_desc_qstat);
} else {
desc = &virtnet_sq_stats_desc_qstat[0];
num = ARRAY_SIZE(virtnet_sq_stats_desc_qstat);
}
for (i = 0; i < num; ++i) {
offset = desc[i].qstat_offset / sizeof(*ctx->data);
v_stat = (const u64_stats_t *)(base + desc[i].offset);
ctx->data[offset] = u64_stats_read(v_stat);
}
return;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_RX_BASIC) {
desc = &virtnet_stats_rx_basic_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_rx_basic_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_RX_BASIC)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_RX_CSUM) {
desc = &virtnet_stats_rx_csum_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_rx_csum_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_RX_CSUM)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_RX_GSO) {
desc = &virtnet_stats_rx_gso_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_rx_gso_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_RX_GSO)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_RX_SPEED) {
desc = &virtnet_stats_rx_speed_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_rx_speed_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_RX_SPEED)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_TX_BASIC) {
desc = &virtnet_stats_tx_basic_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_tx_basic_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_TX_BASIC)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_TX_CSUM) {
desc = &virtnet_stats_tx_csum_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_tx_csum_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_TX_CSUM)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_TX_GSO) {
desc = &virtnet_stats_tx_gso_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_tx_gso_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_TX_GSO)
goto found;
}
if (bitmap & VIRTIO_NET_STATS_TYPE_TX_SPEED) {
desc = &virtnet_stats_tx_speed_desc_qstat[0];
num = ARRAY_SIZE(virtnet_stats_tx_speed_desc_qstat);
if (reply_type == VIRTIO_NET_STATS_TYPE_REPLY_TX_SPEED)
goto found;
}
return;
found:
for (i = 0; i < num; ++i) {
offset = desc[i].qstat_offset / sizeof(*ctx->data);
v = (const __le64 *)(base + desc[i].offset);
ctx->data[offset] = le64_to_cpu(*v);
}
}
/* virtnet_fill_stats - copy the stats to qstats or ethtool -S
* The stats source is the device or the driver.
*
* @vi: virtio net info
@ -3611,6 +3855,9 @@ static void virtnet_fill_stats(struct virtnet_info *vi, u32 qid,
const __le64 *v;
int i, num;
if (ctx->to_qstat)
return virtnet_fill_stats_qstat(vi, qid, ctx, base, drv_stats, reply_type);
num_cq = ctx->desc_num[VIRTNET_Q_TYPE_CQ];
num_rx = ctx->desc_num[VIRTNET_Q_TYPE_RX];
num_tx = ctx->desc_num[VIRTNET_Q_TYPE_TX];
@ -3770,22 +4017,34 @@ static void virtnet_make_stat_req(struct virtnet_info *vi,
*idx += 1;
}
/* qid: -1: get stats of all vq.
* > 0: get the stats for the special vq. This must not be cvq.
*/
static int virtnet_get_hw_stats(struct virtnet_info *vi,
struct virtnet_stats_ctx *ctx)
struct virtnet_stats_ctx *ctx, int qid)
{
int qnum, i, j, res_size, qtype, last_vq, first_vq;
struct virtio_net_ctrl_queue_stats *req;
int qnum, i, j, res_size, qtype, last_vq;
bool enable_cvq;
void *reply;
int ok;
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_DEVICE_STATS))
return 0;
last_vq = vi->curr_queue_pairs * 2 - 1;
if (qid == -1) {
last_vq = vi->curr_queue_pairs * 2 - 1;
first_vq = 0;
enable_cvq = true;
} else {
last_vq = qid;
first_vq = qid;
enable_cvq = false;
}
qnum = 0;
res_size = 0;
for (i = 0; i <= last_vq ; ++i) {
for (i = first_vq; i <= last_vq ; ++i) {
qtype = vq_type(vi, i);
if (ctx->bitmap[qtype]) {
++qnum;
@ -3793,7 +4052,7 @@ static int virtnet_get_hw_stats(struct virtnet_info *vi,
}
}
if (ctx->bitmap[VIRTNET_Q_TYPE_CQ]) {
if (enable_cvq && ctx->bitmap[VIRTNET_Q_TYPE_CQ]) {
res_size += ctx->size[VIRTNET_Q_TYPE_CQ];
qnum += 1;
}
@ -3809,10 +4068,11 @@ static int virtnet_get_hw_stats(struct virtnet_info *vi,
}
j = 0;
for (i = 0; i <= last_vq ; ++i)
for (i = first_vq; i <= last_vq ; ++i)
virtnet_make_stat_req(vi, ctx, req, i, &j);
virtnet_make_stat_req(vi, ctx, req, vi->max_queue_pairs * 2, &j);
if (enable_cvq)
virtnet_make_stat_req(vi, ctx, req, vi->max_queue_pairs * 2, &j);
ok = __virtnet_get_hw_stats(vi, ctx, req, sizeof(*req) * j, reply, res_size);
@ -3853,7 +4113,7 @@ static int virtnet_get_sset_count(struct net_device *dev, int sset)
switch (sset) {
case ETH_SS_STATS:
virtnet_stats_ctx_init(vi, &ctx, NULL);
virtnet_stats_ctx_init(vi, &ctx, NULL, false);
pair_count = ctx.desc_num[VIRTNET_Q_TYPE_RX] + ctx.desc_num[VIRTNET_Q_TYPE_TX];
@ -3872,8 +4132,8 @@ static void virtnet_get_ethtool_stats(struct net_device *dev,
unsigned int start, i;
const u8 *stats_base;
virtnet_stats_ctx_init(vi, &ctx, data);
if (virtnet_get_hw_stats(vi, &ctx))
virtnet_stats_ctx_init(vi, &ctx, data, false);
if (virtnet_get_hw_stats(vi, &ctx, -1))
dev_warn(&vi->dev->dev, "Failed to get hw stats.\n");
for (i = 0; i < vi->curr_queue_pairs; i++) {
@ -4428,6 +4688,95 @@ static const struct ethtool_ops virtnet_ethtool_ops = {
.set_rxnfc = virtnet_set_rxnfc,
};
static void virtnet_get_queue_stats_rx(struct net_device *dev, int i,
struct netdev_queue_stats_rx *stats)
{
struct virtnet_info *vi = netdev_priv(dev);
struct receive_queue *rq = &vi->rq[i];
struct virtnet_stats_ctx ctx = {0};
virtnet_stats_ctx_init(vi, &ctx, (void *)stats, true);
virtnet_get_hw_stats(vi, &ctx, i * 2);
virtnet_fill_stats(vi, i * 2, &ctx, (void *)&rq->stats, true, 0);
}
static void virtnet_get_queue_stats_tx(struct net_device *dev, int i,
struct netdev_queue_stats_tx *stats)
{
struct virtnet_info *vi = netdev_priv(dev);
struct send_queue *sq = &vi->sq[i];
struct virtnet_stats_ctx ctx = {0};
virtnet_stats_ctx_init(vi, &ctx, (void *)stats, true);
virtnet_get_hw_stats(vi, &ctx, i * 2 + 1);
virtnet_fill_stats(vi, i * 2 + 1, &ctx, (void *)&sq->stats, true, 0);
}
static void virtnet_get_base_stats(struct net_device *dev,
struct netdev_queue_stats_rx *rx,
struct netdev_queue_stats_tx *tx)
{
struct virtnet_info *vi = netdev_priv(dev);
/* The queue stats of the virtio-net will not be reset. So here we
* return 0.
*/
rx->bytes = 0;
rx->packets = 0;
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_BASIC) {
rx->hw_drops = 0;
rx->hw_drop_overruns = 0;
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_CSUM) {
rx->csum_unnecessary = 0;
rx->csum_none = 0;
rx->csum_bad = 0;
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_GSO) {
rx->hw_gro_packets = 0;
rx->hw_gro_bytes = 0;
rx->hw_gro_wire_packets = 0;
rx->hw_gro_wire_bytes = 0;
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_SPEED)
rx->hw_drop_ratelimits = 0;
tx->bytes = 0;
tx->packets = 0;
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_BASIC) {
tx->hw_drops = 0;
tx->hw_drop_errors = 0;
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_CSUM) {
tx->csum_none = 0;
tx->needs_csum = 0;
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_GSO) {
tx->hw_gso_packets = 0;
tx->hw_gso_bytes = 0;
tx->hw_gso_wire_packets = 0;
tx->hw_gso_wire_bytes = 0;
}
if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_TX_SPEED)
tx->hw_drop_ratelimits = 0;
}
static const struct netdev_stat_ops virtnet_stat_ops = {
.get_queue_stats_rx = virtnet_get_queue_stats_rx,
.get_queue_stats_tx = virtnet_get_queue_stats_tx,
.get_base_stats = virtnet_get_base_stats,
};
static void virtnet_freeze_down(struct virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
@ -5231,6 +5580,7 @@ static int virtnet_probe(struct virtio_device *vdev)
dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE |
IFF_TX_SKB_NO_LINEAR;
dev->netdev_ops = &virtnet_netdev;
dev->stat_ops = &virtnet_stat_ops;
dev->features = NETIF_F_HIGHDMA;
dev->ethtool_ops = &virtnet_ethtool_ops;