diff options
Diffstat (limited to 'drivers/net/ifb.c')
| -rw-r--r-- | drivers/net/ifb.c | 128 | 
1 files changed, 81 insertions, 47 deletions
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index ab9f675c5b8..46a7790be00 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -32,28 +32,25 @@  #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> -#define TX_TIMEOUT  (2*HZ) -  #define TX_Q_LIMIT    32  struct ifb_private {  	struct tasklet_struct   ifb_tasklet;  	int     tasklet_pending; -	/* mostly debug stats leave in for now */ -	unsigned long   st_task_enter; /* tasklet entered */ -	unsigned long   st_txq_refl_try; /* transmit queue refill attempt */ -	unsigned long   st_rxq_enter; /* receive queue entered */ -	unsigned long   st_rx2tx_tran; /* receive to trasmit transfers */ -	unsigned long   st_rxq_notenter; /*receiveQ not entered, resched */ -	unsigned long   st_rx_frm_egr; /* received from egress path */ -	unsigned long   st_rx_frm_ing; /* received from ingress path */ -	unsigned long   st_rxq_check; -	unsigned long   st_rxq_rsch; + +	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; @@ -65,69 +62,61 @@ 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;  	txq = netdev_get_tx_queue(_dev, 0); -	dp->st_task_enter++;  	if ((skb = skb_peek(&dp->tq)) == NULL) { -		dp->st_txq_refl_try++;  		if (__netif_tx_trylock(txq)) { -			dp->st_rxq_enter++; -			while ((skb = skb_dequeue(&dp->rq)) != NULL) { -				skb_queue_tail(&dp->tq, skb); -				dp->st_rx2tx_tran++; -			} +			skb_queue_splice_tail_init(&dp->rq, &dp->tq);  			__netif_tx_unlock(txq);  		} else {  			/* reschedule */ -			dp->st_rxq_notenter++;  			goto resched;  		}  	} -	while ((skb = skb_dequeue(&dp->tq)) != NULL) { +	while ((skb = __skb_dequeue(&dp->tq)) != NULL) {  		u32 from = G_TC_FROM(skb->tc_verd);  		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;  		}  		rcu_read_unlock();  		skb->skb_iif = _dev->ifindex;  		if (from & AT_EGRESS) { -			dp->st_rx_frm_egr++;  			dev_queue_xmit(skb);  		} else if (from & AT_INGRESS) { -			dp->st_rx_frm_ing++;  			skb_pull(skb, skb->dev->hard_header_len); -			netif_rx(skb); +			netif_receive_skb(skb);  		} else  			BUG();  	}  	if (__netif_tx_trylock(txq)) { -		dp->st_rxq_check++;  		if ((skb = skb_peek(&dp->rq)) == NULL) {  			dp->tasklet_pending = 0;  			if (netif_queue_stopped(_dev))  				netif_wake_queue(_dev);  		} else { -			dp->st_rxq_rsch++;  			__netif_tx_unlock(txq);  			goto resched;  		} @@ -140,13 +129,46 @@ 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_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_CTAG_TX		| \ +		      NETIF_F_HW_VLAN_STAG_TX) +  static void ifb_setup(struct net_device *dev)  {  	/* Initialize the device structure. */ @@ -157,24 +179,29 @@ static void ifb_setup(struct net_device *dev)  	ether_setup(dev);  	dev->tx_queue_len = TX_Q_LIMIT; +	dev->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;  	} @@ -182,7 +209,7 @@ static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)  		netif_stop_queue(dev);  	} -	skb_queue_tail(&dp->rq, skb); +	__skb_queue_tail(&dp->rq, skb);  	if (!dp->tasklet_pending) {  		dp->tasklet_pending = 1;  		tasklet_schedule(&dp->ifb_tasklet); @@ -197,8 +224,8 @@ static int ifb_close(struct net_device *dev)  	tasklet_kill(&dp->ifb_tasklet);  	netif_stop_queue(dev); -	skb_queue_purge(&dp->rq); -	skb_queue_purge(&dp->tq); +	__skb_queue_purge(&dp->rq); +	__skb_queue_purge(&dp->tq);  	return 0;  } @@ -207,8 +234,8 @@ static int ifb_open(struct net_device *dev)  	struct ifb_private *dp = netdev_priv(dev);  	tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev); -	skb_queue_head_init(&dp->rq); -	skb_queue_head_init(&dp->tq); +	__skb_queue_head_init(&dp->rq); +	__skb_queue_head_init(&dp->tq);  	netif_start_queue(dev);  	return 0; @@ -239,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), @@ -247,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); @@ -269,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;  | 
