net: dev ipv4/v6 stat

Upstream: no

Add ipv4/ipv6 pkts and bytes statistics for different netdev.
The output is like this:

cat /proc/net/dev_ext
Interface   ipv4_rx_pkts ipv4_rx_bytes ipv4_tx_pkts ipv4_tx_bytes ipv6_rx_pkts ipv6_rx_bytes ipv6_tx_pkts ipv6_tx_bytes
      eth1          15082      1273185         5805      1291518         2515       194772            0            0
        lo             59         4520           59         4520            0            0            0            0
      eth0              0            0            0            0            0            0            0            0
       br0            309        54452           35         2100          216        24555            0            0

Signed-off-by: Hongbo Li <herberthbli@tencent.com>
Signed-off-by: katrinzhou <katrinzhou@tencent.com>
Signed-off-by: Kairui Song <kasong@tencent.com>
This commit is contained in:
Hongbo Li 2023-11-16 18:26:03 +08:00 committed by Kairui Song
parent 8a4d973e43
commit d9324ddd53
8 changed files with 110 additions and 3 deletions

View File

@ -86,6 +86,7 @@ void synchronize_net(void);
void netdev_set_default_ethtool_ops(struct net_device *dev,
const struct ethtool_ops *ops);
void netdev_sw_irq_coalesce_default_on(struct net_device *dev);
extern unsigned long snmp_fold_field(void __percpu *mib, int offt);
/* Backlog congestion levels */
#define NET_RX_SUCCESS 0 /* keep 'em coming, baby */
@ -1673,6 +1674,15 @@ struct net_device_ops {
KABI_RESERVE(8);
};
#define DEV_MIB_MAX __DEV_MIB_MAX
struct dev_mib {
unsigned long mibs[DEV_MIB_MAX];
};
struct netdev_mib {
DEFINE_SNMP_STAT(struct dev_mib, dev_statistics);
};
/**
* enum netdev_priv_flags - &struct net_device priv_flags
*
@ -2158,6 +2168,7 @@ struct net_device {
int group;
struct net_device_stats stats; /* not used by modern drivers */
struct netdev_mib mib;
struct net_device_core_stats __percpu *core_stats;

View File

@ -380,4 +380,17 @@ enum {
__LINUX_DROPSTAT_MIB_MAX
};
enum {
DEV_MIB_NUM = 0,
DEV_MIB_IPV4_RX_PKTS,
DEV_MIB_IPV4_RX_BYTES,
DEV_MIB_IPV4_TX_PKTS,
DEV_MIB_IPV4_TX_BYTES,
DEV_MIB_IPV6_RX_PKTS,
DEV_MIB_IPV6_RX_BYTES,
DEV_MIB_IPV6_TX_PKTS,
DEV_MIB_IPV6_TX_BYTES,
__DEV_MIB_MAX
};
#endif /* _LINUX_SNMP_H */

View File

@ -10779,9 +10779,13 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
refcount_set(&dev->dev_refcnt, 1);
#endif
if (dev_addr_init(dev))
dev->mib.dev_statistics = alloc_percpu(struct dev_mib);
if (!dev->mib.dev_statistics)
goto free_pcpu;
if (dev_addr_init(dev))
goto free_dev_mib;
dev_mc_init(dev);
dev_uc_init(dev);
@ -10845,7 +10849,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
free_all:
free_netdev(dev);
return NULL;
free_dev_mib:
free_percpu(dev->mib.dev_statistics);
free_pcpu:
#ifdef CONFIG_PCPU_DEV_REFCNT
free_percpu(dev->pcpu_refcnt);
@ -10895,6 +10900,7 @@ void free_netdev(struct net_device *dev)
ref_tracker_dir_exit(&dev->refcnt_tracker);
#ifdef CONFIG_PCPU_DEV_REFCNT
free_percpu(dev->pcpu_refcnt);
free_percpu(dev->mib.dev_statistics);
dev->pcpu_refcnt = NULL;
#endif
free_percpu(dev->core_stats);

View File

@ -320,6 +320,57 @@ static const struct seq_operations ptype_seq_ops = {
.show = ptype_seq_show,
};
static const struct snmp_mib snmp_devstat_list[] = {
SNMP_MIB_ITEM("ipv4_rx_pkts", DEV_MIB_IPV4_RX_PKTS),
SNMP_MIB_ITEM("ipv4_rx_bytes", DEV_MIB_IPV4_RX_BYTES),
SNMP_MIB_ITEM("ipv4_tx_pkts", DEV_MIB_IPV4_TX_PKTS),
SNMP_MIB_ITEM("ipv4_tx_bytes", DEV_MIB_IPV4_TX_BYTES),
SNMP_MIB_ITEM("ipv6_rx_pkts", DEV_MIB_IPV6_RX_PKTS),
SNMP_MIB_ITEM("ipv6_rx_bytes", DEV_MIB_IPV6_RX_BYTES),
SNMP_MIB_ITEM("ipv6_tx_pkts", DEV_MIB_IPV6_TX_PKTS),
SNMP_MIB_ITEM("ipv6_tx_bytes", DEV_MIB_IPV6_TX_BYTES),
SNMP_MIB_SENTINEL
};
static void dev_ext_seq_printf_stats(struct seq_file *seq, void *v)
{
struct net_device *dev = (struct net_device *)v;
int i;
seq_printf(seq, "%10s ", dev->name);
for (i = 0; snmp_devstat_list[i].name; i++)
seq_printf(seq, " %12lu",
snmp_fold_field((void __percpu **)
dev->mib.dev_statistics,
snmp_devstat_list[i].entry));
seq_putc(seq, '\n');
}
static int dev_ext_seq_show(struct seq_file *seq, void *v)
{
int i;
if (v == SEQ_START_TOKEN) {
seq_puts(seq, "Interface ");
for (i = 0; snmp_devstat_list[i].name; i++)
seq_printf(seq, "%s ", snmp_devstat_list[i].name);
seq_putc(seq, '\n');
} else {
dev_ext_seq_printf_stats(seq, v);
}
return 0;
}
static const struct seq_operations dev_ext_seq_ops = {
.start = dev_seq_start,
.next = dev_seq_next,
.stop = dev_seq_stop,
.show = dev_ext_seq_show,
};
static int __net_init dev_proc_net_init(struct net *net)
{
int rc = -ENOMEM;
@ -327,9 +378,12 @@ static int __net_init dev_proc_net_init(struct net *net)
if (!proc_create_net("dev", 0444, net->proc_net, &dev_seq_ops,
sizeof(struct seq_net_private)))
goto out;
if (!proc_create_net("dev_ext", 0444, net->proc_net, &dev_ext_seq_ops,
sizeof(struct seq_net_private)))
goto out_dev;
if (!proc_create_seq("softnet_stat", 0444, net->proc_net,
&softnet_seq_ops))
goto out_dev;
goto out_dev_ext;
if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
sizeof(struct seq_net_private)))
goto out_softnet;
@ -343,6 +397,8 @@ out_ptype:
remove_proc_entry("ptype", net->proc_net);
out_softnet:
remove_proc_entry("softnet_stat", net->proc_net);
out_dev_ext:
remove_proc_entry("dev_ext", net->proc_net);
out_dev:
remove_proc_entry("dev", net->proc_net);
goto out;
@ -354,6 +410,7 @@ static void __net_exit dev_proc_net_exit(struct net *net)
remove_proc_entry("ptype", net->proc_net);
remove_proc_entry("softnet_stat", net->proc_net);
remove_proc_entry("dev_ext", net->proc_net);
remove_proc_entry("dev", net->proc_net);
}

View File

@ -469,6 +469,12 @@ static struct sk_buff *ip_rcv_core(struct sk_buff *skb, struct net *net)
}
__IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);
if (skb->dev) {
__SNMP_INC_STATS(skb->dev->mib.dev_statistics,
DEV_MIB_IPV4_RX_PKTS);
__SNMP_ADD_STATS(skb->dev->mib.dev_statistics,
DEV_MIB_IPV4_RX_BYTES, skb->len);
}
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb) {

View File

@ -202,6 +202,10 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
struct neighbour *neigh;
bool is_v6gw = false;
SNMP_INC_STATS(dev->mib.dev_statistics, DEV_MIB_IPV4_TX_PKTS);
SNMP_ADD_STATS(dev->mib.dev_statistics, DEV_MIB_IPV4_TX_BYTES,
skb->len);
if (rt->rt_type == RTN_MULTICAST) {
IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTMCAST, skb->len);
} else if (rt->rt_type == RTN_BROADCAST)

View File

@ -160,6 +160,12 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
return NULL;
}
if (dev) {
__SNMP_INC_STATS(dev->mib.dev_statistics, DEV_MIB_IPV6_RX_PKTS);
__SNMP_ADD_STATS(dev->mib.dev_statistics, DEV_MIB_IPV6_RX_BYTES,
skb->len);
}
rcu_read_lock();
idev = __in6_dev_get(skb->dev);

View File

@ -68,6 +68,10 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
struct neighbour *neigh;
int ret;
SNMP_INC_STATS(dev->mib.dev_statistics, DEV_MIB_IPV6_TX_PKTS);
SNMP_ADD_STATS(dev->mib.dev_statistics, DEV_MIB_IPV6_TX_BYTES,
skb->len);
/* Be paranoid, rather than too clever. */
if (unlikely(hh_len > skb_headroom(skb)) && dev->header_ops) {
skb = skb_expand_head(skb, hh_len);