Commit 497a5757 authored by Heiner Kallweit's avatar Heiner Kallweit Committed by Jakub Kicinski

tun: switch to net core provided statistics counters

Switch tun to the standard statistics pattern:
- use netdev->stats for the less frequently accessed counters
- use netdev->tstats for the frequently accessed per-cpu counters

v3:
- add atomic_long_t member rx_frame_errors for making counter updates
  atomic
Signed-off-by: default avatarHeiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 6a900628
...@@ -107,17 +107,6 @@ struct tap_filter { ...@@ -107,17 +107,6 @@ struct tap_filter {
#define TUN_FLOW_EXPIRE (3 * HZ) #define TUN_FLOW_EXPIRE (3 * HZ)
struct tun_pcpu_stats {
u64_stats_t rx_packets;
u64_stats_t rx_bytes;
u64_stats_t tx_packets;
u64_stats_t tx_bytes;
struct u64_stats_sync syncp;
u32 rx_dropped;
u32 tx_dropped;
u32 rx_frame_errors;
};
/* A tun_file connects an open character device to a tuntap netdevice. It /* A tun_file connects an open character device to a tuntap netdevice. It
* also contains all socket related structures (except sock_fprog and tap_filter) * also contains all socket related structures (except sock_fprog and tap_filter)
* to serve as one transmit queue for tuntap device. The sock_fprog and * to serve as one transmit queue for tuntap device. The sock_fprog and
...@@ -207,7 +196,7 @@ struct tun_struct { ...@@ -207,7 +196,7 @@ struct tun_struct {
void *security; void *security;
u32 flow_count; u32 flow_count;
u32 rx_batched; u32 rx_batched;
struct tun_pcpu_stats __percpu *pcpu_stats; atomic_long_t rx_frame_errors;
struct bpf_prog __rcu *xdp_prog; struct bpf_prog __rcu *xdp_prog;
struct tun_prog __rcu *steering_prog; struct tun_prog __rcu *steering_prog;
struct tun_prog __rcu *filter_prog; struct tun_prog __rcu *filter_prog;
...@@ -1066,7 +1055,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1066,7 +1055,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK; return NETDEV_TX_OK;
drop: drop:
this_cpu_inc(tun->pcpu_stats->tx_dropped); atomic_long_inc(&dev->tx_dropped);
skb_tx_error(skb); skb_tx_error(skb);
kfree_skb(skb); kfree_skb(skb);
rcu_read_unlock(); rcu_read_unlock();
...@@ -1103,37 +1092,12 @@ static void tun_set_headroom(struct net_device *dev, int new_hr) ...@@ -1103,37 +1092,12 @@ static void tun_set_headroom(struct net_device *dev, int new_hr)
static void static void
tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) tun_net_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{ {
u32 rx_dropped = 0, tx_dropped = 0, rx_frame_errors = 0;
struct tun_struct *tun = netdev_priv(dev); struct tun_struct *tun = netdev_priv(dev);
struct tun_pcpu_stats *p;
int i;
for_each_possible_cpu(i) {
u64 rxpackets, rxbytes, txpackets, txbytes;
unsigned int start;
p = per_cpu_ptr(tun->pcpu_stats, i); dev_get_tstats64(dev, stats);
do {
start = u64_stats_fetch_begin(&p->syncp);
rxpackets = u64_stats_read(&p->rx_packets);
rxbytes = u64_stats_read(&p->rx_bytes);
txpackets = u64_stats_read(&p->tx_packets);
txbytes = u64_stats_read(&p->tx_bytes);
} while (u64_stats_fetch_retry(&p->syncp, start));
stats->rx_packets += rxpackets; stats->rx_frame_errors +=
stats->rx_bytes += rxbytes; (unsigned long)atomic_long_read(&tun->rx_frame_errors);
stats->tx_packets += txpackets;
stats->tx_bytes += txbytes;
/* u32 counters */
rx_dropped += p->rx_dropped;
rx_frame_errors += p->rx_frame_errors;
tx_dropped += p->tx_dropped;
}
stats->rx_dropped = rx_dropped;
stats->rx_frame_errors = rx_frame_errors;
stats->tx_dropped = tx_dropped;
} }
static int tun_xdp_set(struct net_device *dev, struct bpf_prog *prog, static int tun_xdp_set(struct net_device *dev, struct bpf_prog *prog,
...@@ -1247,7 +1211,7 @@ static int tun_xdp_xmit(struct net_device *dev, int n, ...@@ -1247,7 +1211,7 @@ static int tun_xdp_xmit(struct net_device *dev, int n,
void *frame = tun_xdp_to_ptr(xdp); void *frame = tun_xdp_to_ptr(xdp);
if (__ptr_ring_produce(&tfile->tx_ring, frame)) { if (__ptr_ring_produce(&tfile->tx_ring, frame)) {
this_cpu_inc(tun->pcpu_stats->tx_dropped); atomic_long_inc(&dev->tx_dropped);
xdp_return_frame_rx_napi(xdp); xdp_return_frame_rx_napi(xdp);
drops++; drops++;
} }
...@@ -1283,7 +1247,7 @@ static const struct net_device_ops tap_netdev_ops = { ...@@ -1283,7 +1247,7 @@ static const struct net_device_ops tap_netdev_ops = {
.ndo_select_queue = tun_select_queue, .ndo_select_queue = tun_select_queue,
.ndo_features_check = passthru_features_check, .ndo_features_check = passthru_features_check,
.ndo_set_rx_headroom = tun_set_headroom, .ndo_set_rx_headroom = tun_set_headroom,
.ndo_get_stats64 = tun_net_get_stats64, .ndo_get_stats64 = dev_get_tstats64,
.ndo_bpf = tun_xdp, .ndo_bpf = tun_xdp,
.ndo_xdp_xmit = tun_xdp_xmit, .ndo_xdp_xmit = tun_xdp_xmit,
.ndo_change_carrier = tun_net_change_carrier, .ndo_change_carrier = tun_net_change_carrier,
...@@ -1577,7 +1541,7 @@ static int tun_xdp_act(struct tun_struct *tun, struct bpf_prog *xdp_prog, ...@@ -1577,7 +1541,7 @@ static int tun_xdp_act(struct tun_struct *tun, struct bpf_prog *xdp_prog,
trace_xdp_exception(tun->dev, xdp_prog, act); trace_xdp_exception(tun->dev, xdp_prog, act);
fallthrough; fallthrough;
case XDP_DROP: case XDP_DROP:
this_cpu_inc(tun->pcpu_stats->rx_dropped); atomic_long_inc(&tun->dev->rx_dropped);
break; break;
} }
...@@ -1683,7 +1647,6 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1683,7 +1647,6 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
size_t total_len = iov_iter_count(from); size_t total_len = iov_iter_count(from);
size_t len = total_len, align = tun->align, linear; size_t len = total_len, align = tun->align, linear;
struct virtio_net_hdr gso = { 0 }; struct virtio_net_hdr gso = { 0 };
struct tun_pcpu_stats *stats;
int good_linear; int good_linear;
int copylen; int copylen;
bool zerocopy = false; bool zerocopy = false;
...@@ -1752,7 +1715,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1752,7 +1715,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
*/ */
skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp); skb = tun_build_skb(tun, tfile, from, &gso, len, &skb_xdp);
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
this_cpu_inc(tun->pcpu_stats->rx_dropped); atomic_long_inc(&tun->dev->rx_dropped);
return PTR_ERR(skb); return PTR_ERR(skb);
} }
if (!skb) if (!skb)
...@@ -1781,7 +1744,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1781,7 +1744,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
if (IS_ERR(skb)) { if (IS_ERR(skb)) {
if (PTR_ERR(skb) != -EAGAIN) if (PTR_ERR(skb) != -EAGAIN)
this_cpu_inc(tun->pcpu_stats->rx_dropped); atomic_long_inc(&tun->dev->rx_dropped);
if (frags) if (frags)
mutex_unlock(&tfile->napi_mutex); mutex_unlock(&tfile->napi_mutex);
return PTR_ERR(skb); return PTR_ERR(skb);
...@@ -1795,7 +1758,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1795,7 +1758,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
if (err) { if (err) {
err = -EFAULT; err = -EFAULT;
drop: drop:
this_cpu_inc(tun->pcpu_stats->rx_dropped); atomic_long_inc(&tun->dev->rx_dropped);
kfree_skb(skb); kfree_skb(skb);
if (frags) { if (frags) {
tfile->napi.skb = NULL; tfile->napi.skb = NULL;
...@@ -1807,7 +1770,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1807,7 +1770,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
} }
if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) {
this_cpu_inc(tun->pcpu_stats->rx_frame_errors); atomic_long_inc(&tun->rx_frame_errors);
kfree_skb(skb); kfree_skb(skb);
if (frags) { if (frags) {
tfile->napi.skb = NULL; tfile->napi.skb = NULL;
...@@ -1830,7 +1793,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1830,7 +1793,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
pi.proto = htons(ETH_P_IPV6); pi.proto = htons(ETH_P_IPV6);
break; break;
default: default:
this_cpu_inc(tun->pcpu_stats->rx_dropped); atomic_long_inc(&tun->dev->rx_dropped);
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -1910,7 +1873,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1910,7 +1873,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
skb_headlen(skb)); skb_headlen(skb));
if (unlikely(headlen > skb_headlen(skb))) { if (unlikely(headlen > skb_headlen(skb))) {
this_cpu_inc(tun->pcpu_stats->rx_dropped); atomic_long_inc(&tun->dev->rx_dropped);
napi_free_frags(&tfile->napi); napi_free_frags(&tfile->napi);
rcu_read_unlock(); rcu_read_unlock();
mutex_unlock(&tfile->napi_mutex); mutex_unlock(&tfile->napi_mutex);
...@@ -1942,12 +1905,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, ...@@ -1942,12 +1905,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
} }
rcu_read_unlock(); rcu_read_unlock();
stats = get_cpu_ptr(tun->pcpu_stats); preempt_disable();
u64_stats_update_begin(&stats->syncp); dev_sw_netstats_rx_add(tun->dev, len);
u64_stats_inc(&stats->rx_packets); preempt_enable();
u64_stats_add(&stats->rx_bytes, len);
u64_stats_update_end(&stats->syncp);
put_cpu_ptr(stats);
if (rxhash) if (rxhash)
tun_flow_update(tun, rxhash, tfile); tun_flow_update(tun, rxhash, tfile);
...@@ -1979,7 +1939,6 @@ static ssize_t tun_put_user_xdp(struct tun_struct *tun, ...@@ -1979,7 +1939,6 @@ static ssize_t tun_put_user_xdp(struct tun_struct *tun,
{ {
int vnet_hdr_sz = 0; int vnet_hdr_sz = 0;
size_t size = xdp_frame->len; size_t size = xdp_frame->len;
struct tun_pcpu_stats *stats;
size_t ret; size_t ret;
if (tun->flags & IFF_VNET_HDR) { if (tun->flags & IFF_VNET_HDR) {
...@@ -1996,12 +1955,9 @@ static ssize_t tun_put_user_xdp(struct tun_struct *tun, ...@@ -1996,12 +1955,9 @@ static ssize_t tun_put_user_xdp(struct tun_struct *tun,
ret = copy_to_iter(xdp_frame->data, size, iter) + vnet_hdr_sz; ret = copy_to_iter(xdp_frame->data, size, iter) + vnet_hdr_sz;
stats = get_cpu_ptr(tun->pcpu_stats); preempt_disable();
u64_stats_update_begin(&stats->syncp); dev_sw_netstats_tx_add(tun->dev, 1, ret);
u64_stats_inc(&stats->tx_packets); preempt_enable();
u64_stats_add(&stats->tx_bytes, ret);
u64_stats_update_end(&stats->syncp);
put_cpu_ptr(tun->pcpu_stats);
return ret; return ret;
} }
...@@ -2013,7 +1969,6 @@ static ssize_t tun_put_user(struct tun_struct *tun, ...@@ -2013,7 +1969,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
struct iov_iter *iter) struct iov_iter *iter)
{ {
struct tun_pi pi = { 0, skb->protocol }; struct tun_pi pi = { 0, skb->protocol };
struct tun_pcpu_stats *stats;
ssize_t total; ssize_t total;
int vlan_offset = 0; int vlan_offset = 0;
int vlan_hlen = 0; int vlan_hlen = 0;
...@@ -2091,12 +2046,9 @@ static ssize_t tun_put_user(struct tun_struct *tun, ...@@ -2091,12 +2046,9 @@ static ssize_t tun_put_user(struct tun_struct *tun,
done: done:
/* caller is in process context, */ /* caller is in process context, */
stats = get_cpu_ptr(tun->pcpu_stats); preempt_disable();
u64_stats_update_begin(&stats->syncp); dev_sw_netstats_tx_add(tun->dev, 1, skb->len + vlan_hlen);
u64_stats_inc(&stats->tx_packets); preempt_enable();
u64_stats_add(&stats->tx_bytes, skb->len + vlan_hlen);
u64_stats_update_end(&stats->syncp);
put_cpu_ptr(tun->pcpu_stats);
return total; return total;
} }
...@@ -2235,11 +2187,11 @@ static void tun_free_netdev(struct net_device *dev) ...@@ -2235,11 +2187,11 @@ static void tun_free_netdev(struct net_device *dev)
BUG_ON(!(list_empty(&tun->disabled))); BUG_ON(!(list_empty(&tun->disabled)));
free_percpu(tun->pcpu_stats); free_percpu(dev->tstats);
/* We clear pcpu_stats so that tun_set_iff() can tell if /* We clear tstats so that tun_set_iff() can tell if
* tun_free_netdev() has been called from register_netdevice(). * tun_free_netdev() has been called from register_netdevice().
*/ */
tun->pcpu_stats = NULL; dev->tstats = NULL;
tun_flow_uninit(tun); tun_flow_uninit(tun);
security_tun_dev_free_security(tun->security); security_tun_dev_free_security(tun->security);
...@@ -2370,7 +2322,6 @@ static int tun_xdp_one(struct tun_struct *tun, ...@@ -2370,7 +2322,6 @@ static int tun_xdp_one(struct tun_struct *tun,
unsigned int datasize = xdp->data_end - xdp->data; unsigned int datasize = xdp->data_end - xdp->data;
struct tun_xdp_hdr *hdr = xdp->data_hard_start; struct tun_xdp_hdr *hdr = xdp->data_hard_start;
struct virtio_net_hdr *gso = &hdr->gso; struct virtio_net_hdr *gso = &hdr->gso;
struct tun_pcpu_stats *stats;
struct bpf_prog *xdp_prog; struct bpf_prog *xdp_prog;
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
u32 rxhash = 0, act; u32 rxhash = 0, act;
...@@ -2428,7 +2379,7 @@ static int tun_xdp_one(struct tun_struct *tun, ...@@ -2428,7 +2379,7 @@ static int tun_xdp_one(struct tun_struct *tun,
skb_put(skb, xdp->data_end - xdp->data); skb_put(skb, xdp->data_end - xdp->data);
if (virtio_net_hdr_to_skb(skb, gso, tun_is_little_endian(tun))) { if (virtio_net_hdr_to_skb(skb, gso, tun_is_little_endian(tun))) {
this_cpu_inc(tun->pcpu_stats->rx_frame_errors); atomic_long_inc(&tun->rx_frame_errors);
kfree_skb(skb); kfree_skb(skb);
err = -EINVAL; err = -EINVAL;
goto out; goto out;
...@@ -2451,14 +2402,10 @@ static int tun_xdp_one(struct tun_struct *tun, ...@@ -2451,14 +2402,10 @@ static int tun_xdp_one(struct tun_struct *tun,
netif_receive_skb(skb); netif_receive_skb(skb);
/* No need for get_cpu_ptr() here since this function is /* No need to disable preemption here since this function is
* always called with bh disabled * always called with bh disabled
*/ */
stats = this_cpu_ptr(tun->pcpu_stats); dev_sw_netstats_rx_add(tun->dev, datasize);
u64_stats_update_begin(&stats->syncp);
u64_stats_inc(&stats->rx_packets);
u64_stats_add(&stats->rx_bytes, datasize);
u64_stats_update_end(&stats->syncp);
if (rxhash) if (rxhash)
tun_flow_update(tun, rxhash, tfile); tun_flow_update(tun, rxhash, tfile);
...@@ -2751,8 +2698,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) ...@@ -2751,8 +2698,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun->rx_batched = 0; tun->rx_batched = 0;
RCU_INIT_POINTER(tun->steering_prog, NULL); RCU_INIT_POINTER(tun->steering_prog, NULL);
tun->pcpu_stats = netdev_alloc_pcpu_stats(struct tun_pcpu_stats); dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!tun->pcpu_stats) { if (!dev->tstats) {
err = -ENOMEM; err = -ENOMEM;
goto err_free_dev; goto err_free_dev;
} }
...@@ -2807,16 +2754,16 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) ...@@ -2807,16 +2754,16 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun_detach_all(dev); tun_detach_all(dev);
/* We are here because register_netdevice() has failed. /* We are here because register_netdevice() has failed.
* If register_netdevice() already called tun_free_netdev() * If register_netdevice() already called tun_free_netdev()
* while dealing with the error, tun->pcpu_stats has been cleared. * while dealing with the error, dev->stats has been cleared.
*/ */
if (!tun->pcpu_stats) if (!dev->tstats)
goto err_free_dev; goto err_free_dev;
err_free_flow: err_free_flow:
tun_flow_uninit(tun); tun_flow_uninit(tun);
security_tun_dev_free_security(tun->security); security_tun_dev_free_security(tun->security);
err_free_stat: err_free_stat:
free_percpu(tun->pcpu_stats); free_percpu(dev->tstats);
err_free_dev: err_free_dev:
free_netdev(dev); free_netdev(dev);
return err; return err;
......
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