Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: dsa: User per-cpu 64-bit statistics

During testing with a background iperf pushing 1Gbit/sec worth of
traffic and having both ifconfig and ethtool collect statistics, we
could see quite frequent deadlocks. Convert the often accessed DSA slave
network devices statistics to per-cpu 64-bit statistics to remove these
deadlocks and provide fast efficient statistics updates.

Fixes: f613ed665bb3 ("net: dsa: Add support for 64-bit statistics")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Florian Fainelli and committed by
David S. Miller
5f6b4e14 eaf6dc03

+58 -24
+6 -4
net/dsa/dsa.c
··· 190 190 { 191 191 struct dsa_switch_tree *dst = dev->dsa_ptr; 192 192 struct sk_buff *nskb = NULL; 193 + struct pcpu_sw_netstats *s; 193 194 struct dsa_slave_priv *p; 194 195 195 196 if (unlikely(dst == NULL)) { ··· 214 213 skb->pkt_type = PACKET_HOST; 215 214 skb->protocol = eth_type_trans(skb, skb->dev); 216 215 217 - u64_stats_update_begin(&p->stats64.syncp); 218 - p->stats64.rx_packets++; 219 - p->stats64.rx_bytes += skb->len; 220 - u64_stats_update_end(&p->stats64.syncp); 216 + s = this_cpu_ptr(p->stats64); 217 + u64_stats_update_begin(&s->syncp); 218 + s->rx_packets++; 219 + s->rx_bytes += skb->len; 220 + u64_stats_update_end(&s->syncp); 221 221 222 222 netif_receive_skb(skb); 223 223
+1 -1
net/dsa/dsa_priv.h
··· 77 77 struct sk_buff * (*xmit)(struct sk_buff *skb, 78 78 struct net_device *dev); 79 79 80 - struct pcpu_sw_netstats stats64; 80 + struct pcpu_sw_netstats *stats64; 81 81 82 82 /* DSA port data, such as switch, port index, etc. */ 83 83 struct dsa_port *dp;
+51 -19
net/dsa/slave.c
··· 352 352 static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) 353 353 { 354 354 struct dsa_slave_priv *p = netdev_priv(dev); 355 + struct pcpu_sw_netstats *s; 355 356 struct sk_buff *nskb; 356 357 357 - u64_stats_update_begin(&p->stats64.syncp); 358 - p->stats64.tx_packets++; 359 - p->stats64.tx_bytes += skb->len; 360 - u64_stats_update_end(&p->stats64.syncp); 358 + s = this_cpu_ptr(p->stats64); 359 + u64_stats_update_begin(&s->syncp); 360 + s->tx_packets++; 361 + s->tx_bytes += skb->len; 362 + u64_stats_update_end(&s->syncp); 361 363 362 364 /* Transmit function may have to reallocate the original SKB, 363 365 * in which case it must have freed it. Only free it here on error. ··· 598 596 { 599 597 struct dsa_slave_priv *p = netdev_priv(dev); 600 598 struct dsa_switch *ds = p->dp->ds; 599 + struct pcpu_sw_netstats *s; 601 600 unsigned int start; 601 + int i; 602 602 603 - do { 604 - start = u64_stats_fetch_begin_irq(&p->stats64.syncp); 605 - data[0] = p->stats64.tx_packets; 606 - data[1] = p->stats64.tx_bytes; 607 - data[2] = p->stats64.rx_packets; 608 - data[3] = p->stats64.rx_bytes; 609 - } while (u64_stats_fetch_retry_irq(&p->stats64.syncp, start)); 603 + for_each_possible_cpu(i) { 604 + u64 tx_packets, tx_bytes, rx_packets, rx_bytes; 605 + 606 + s = per_cpu_ptr(p->stats64, i); 607 + do { 608 + start = u64_stats_fetch_begin_irq(&s->syncp); 609 + tx_packets = s->tx_packets; 610 + tx_bytes = s->tx_bytes; 611 + rx_packets = s->rx_packets; 612 + rx_bytes = s->rx_bytes; 613 + } while (u64_stats_fetch_retry_irq(&s->syncp, start)); 614 + data[0] += tx_packets; 615 + data[1] += tx_bytes; 616 + data[2] += rx_packets; 617 + data[3] += rx_bytes; 618 + } 610 619 if (ds->ops->get_ethtool_stats) 611 620 ds->ops->get_ethtool_stats(ds, p->dp->index, data + 4); 612 621 } ··· 892 879 struct rtnl_link_stats64 *stats) 893 880 { 894 881 struct dsa_slave_priv *p = netdev_priv(dev); 882 + struct pcpu_sw_netstats *s; 895 883 unsigned int start; 884 + int i; 896 885 897 886 netdev_stats_to_stats64(stats, &dev->stats); 898 - do { 899 - start = u64_stats_fetch_begin_irq(&p->stats64.syncp); 900 - stats->tx_packets = p->stats64.tx_packets; 901 - stats->tx_bytes = p->stats64.tx_bytes; 902 - stats->rx_packets = p->stats64.rx_packets; 903 - stats->rx_bytes = p->stats64.rx_bytes; 904 - } while (u64_stats_fetch_retry_irq(&p->stats64.syncp, start)); 887 + for_each_possible_cpu(i) { 888 + u64 tx_packets, tx_bytes, rx_packets, rx_bytes; 889 + 890 + s = per_cpu_ptr(p->stats64, i); 891 + do { 892 + start = u64_stats_fetch_begin_irq(&s->syncp); 893 + tx_packets = s->tx_packets; 894 + tx_bytes = s->tx_bytes; 895 + rx_packets = s->rx_packets; 896 + rx_bytes = s->rx_bytes; 897 + } while (u64_stats_fetch_retry_irq(&s->syncp, start)); 898 + 899 + stats->tx_packets += tx_packets; 900 + stats->tx_bytes += tx_bytes; 901 + stats->rx_packets += rx_packets; 902 + stats->rx_bytes += rx_bytes; 903 + } 905 904 } 906 905 907 906 void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops) ··· 1227 1202 slave_dev->vlan_features = master->vlan_features; 1228 1203 1229 1204 p = netdev_priv(slave_dev); 1230 - u64_stats_init(&p->stats64.syncp); 1205 + p->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 1206 + if (!p->stats64) { 1207 + free_netdev(slave_dev); 1208 + return -ENOMEM; 1209 + } 1231 1210 p->dp = &ds->ports[port]; 1232 1211 INIT_LIST_HEAD(&p->mall_tc_list); 1233 1212 p->xmit = dst->tag_ops->xmit; ··· 1246 1217 netdev_err(master, "error %d registering interface %s\n", 1247 1218 ret, slave_dev->name); 1248 1219 ds->ports[port].netdev = NULL; 1220 + free_percpu(p->stats64); 1249 1221 free_netdev(slave_dev); 1250 1222 return ret; 1251 1223 } ··· 1257 1227 if (ret) { 1258 1228 netdev_err(master, "error %d setting up slave phy\n", ret); 1259 1229 unregister_netdev(slave_dev); 1230 + free_percpu(p->stats64); 1260 1231 free_netdev(slave_dev); 1261 1232 return ret; 1262 1233 } ··· 1280 1249 of_phy_deregister_fixed_link(port_dn); 1281 1250 } 1282 1251 unregister_netdev(slave_dev); 1252 + free_percpu(p->stats64); 1283 1253 free_netdev(slave_dev); 1284 1254 } 1285 1255