diff options
Diffstat (limited to 'drivers/net/ifb.c')
| -rw-r--r-- | drivers/net/ifb.c | 86 |
1 files changed, 67 insertions, 19 deletions
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index e07d487f015..46a7790be00 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -32,6 +32,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/moduleparam.h> #include <net/pkt_sched.h> #include <net/net_namespace.h> @@ -40,8 +41,16 @@ struct ifb_private { struct tasklet_struct ifb_tasklet; int tasklet_pending; + + struct u64_stats_sync rsync; struct sk_buff_head rq; + u64 rx_packets; + u64 rx_bytes; + + struct u64_stats_sync tsync; struct sk_buff_head tq; + u64 tx_packets; + u64 tx_bytes; }; static int numifbs = 2; @@ -53,10 +62,8 @@ static int ifb_close(struct net_device *dev); static void ri_tasklet(unsigned long dev) { - struct net_device *_dev = (struct net_device *)dev; struct ifb_private *dp = netdev_priv(_dev); - struct net_device_stats *stats = &_dev->stats; struct netdev_queue *txq; struct sk_buff *skb; @@ -76,15 +83,18 @@ static void ri_tasklet(unsigned long dev) skb->tc_verd = 0; skb->tc_verd = SET_TC_NCLS(skb->tc_verd); - stats->tx_packets++; - stats->tx_bytes +=skb->len; + + u64_stats_update_begin(&dp->tsync); + dp->tx_packets++; + dp->tx_bytes += skb->len; + u64_stats_update_end(&dp->tsync); rcu_read_lock(); - skb->dev = dev_get_by_index_rcu(&init_net, skb->skb_iif); + skb->dev = dev_get_by_index_rcu(dev_net(_dev), skb->skb_iif); if (!skb->dev) { rcu_read_unlock(); dev_kfree_skb(skb); - stats->tx_dropped++; + _dev->stats.tx_dropped++; if (skb_queue_len(&dp->tq) != 0) goto resched; break; @@ -119,16 +129,45 @@ resched: } +static struct rtnl_link_stats64 *ifb_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct ifb_private *dp = netdev_priv(dev); + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(&dp->rsync); + stats->rx_packets = dp->rx_packets; + stats->rx_bytes = dp->rx_bytes; + } while (u64_stats_fetch_retry_irq(&dp->rsync, start)); + + do { + start = u64_stats_fetch_begin_irq(&dp->tsync); + + stats->tx_packets = dp->tx_packets; + stats->tx_bytes = dp->tx_bytes; + + } while (u64_stats_fetch_retry_irq(&dp->tsync, start)); + + stats->rx_dropped = dev->stats.rx_dropped; + stats->tx_dropped = dev->stats.tx_dropped; + + return stats; +} + + static const struct net_device_ops ifb_netdev_ops = { .ndo_open = ifb_open, .ndo_stop = ifb_close, + .ndo_get_stats64 = ifb_stats64, .ndo_start_xmit = ifb_xmit, .ndo_validate_addr = eth_validate_addr, }; -#define IFB_FEATURES (NETIF_F_NO_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \ +#define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \ NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \ - NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_TX) + NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX | \ + NETIF_F_HW_VLAN_STAG_TX) static void ifb_setup(struct net_device *dev) { @@ -141,26 +180,28 @@ static void ifb_setup(struct net_device *dev) dev->tx_queue_len = TX_Q_LIMIT; dev->features |= IFB_FEATURES; - dev->vlan_features |= IFB_FEATURES; + dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); dev->flags |= IFF_NOARP; dev->flags &= ~IFF_MULTICAST; - dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; - random_ether_addr(dev->dev_addr); + dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); + eth_hw_addr_random(dev); } static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev) { struct ifb_private *dp = netdev_priv(dev); - struct net_device_stats *stats = &dev->stats; u32 from = G_TC_FROM(skb->tc_verd); - stats->rx_packets++; - stats->rx_bytes+=skb->len; + u64_stats_update_begin(&dp->rsync); + dp->rx_packets++; + dp->rx_bytes += skb->len; + u64_stats_update_end(&dp->rsync); if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->skb_iif) { dev_kfree_skb(skb); - stats->rx_dropped++; + dev->stats.rx_dropped++; return NETDEV_TX_OK; } @@ -225,6 +266,7 @@ MODULE_PARM_DESC(numifbs, "Number of ifb devices"); static int __init ifb_init_one(int index) { struct net_device *dev_ifb; + struct ifb_private *dp; int err; dev_ifb = alloc_netdev(sizeof(struct ifb_private), @@ -233,9 +275,9 @@ static int __init ifb_init_one(int index) if (!dev_ifb) return -ENOMEM; - err = dev_alloc_name(dev_ifb, dev_ifb->name); - if (err < 0) - goto err; + dp = netdev_priv(dev_ifb); + u64_stats_init(&dp->rsync); + u64_stats_init(&dp->tsync); dev_ifb->rtnl_link_ops = &ifb_link_ops; err = register_netdevice(dev_ifb); @@ -255,11 +297,17 @@ static int __init ifb_init_module(void) rtnl_lock(); err = __rtnl_link_register(&ifb_link_ops); + if (err < 0) + goto out; - for (i = 0; i < numifbs && !err; i++) + for (i = 0; i < numifbs && !err; i++) { err = ifb_init_one(i); + cond_resched(); + } if (err) __rtnl_link_unregister(&ifb_link_ops); + +out: rtnl_unlock(); return err; |
