aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorEric Dumazet <dada1@cosmosbay.com>2009-05-19 01:19:19 -0400
committerDavid S. Miller <davem@davemloft.net>2009-05-19 01:19:19 -0400
commit93f154b594fe47e4a7e5358b309add449a046cd3 (patch)
tree479d9f2db25922a2312547c97e73d5a11b68bb1c /net
parent496a60cdcd5d0d884dddf6c3b4ea912923a70f13 (diff)
net: release dst entry in dev_hard_start_xmit()
One point of contention in high network loads is the dst_release() performed when a transmited skb is freed. This is because NIC tx completion calls dev_kree_skb() long after original call to dev_queue_xmit(skb). CPU cache is cold and the atomic op in dst_release() stalls. On SMP, this is quite visible if one CPU is 100% handling softirqs for a network device, since dst_clone() is done by other cpus, involving cache line ping pongs. It seems right place to release dst is in dev_hard_start_xmit(), for most devices but ones that are virtual, and some exceptions. David Miller suggested to define a new device flag, set in alloc_netdev_mq() (so that most devices set it at init time), and carefuly unset in devices which dont want a NULL skb->dst in their ndo_start_xmit(). List of devices that must clear this flag is : - loopback device, because it calls netif_rx() and quoting Patrick : "ip_route_input() doesn't accept loopback addresses, so loopback packets already need to have a dst_entry attached." - appletalk/ipddp.c : needs skb->dst in its xmit function - And all devices that call again dev_queue_xmit() from their xmit function (as some classifiers need skb->dst) : bonding, vlan, macvlan, eql, ifb, hdlc_fr Signed-off-by: Eric Dumazet <dada1@cosmosbay.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/8021q/vlan_dev.c1
-rw-r--r--net/core/dev.c9
2 files changed, 10 insertions, 0 deletions
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 8faacee68633..ff7572ac5481 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -739,6 +739,7 @@ void vlan_setup(struct net_device *dev)
739 ether_setup(dev); 739 ether_setup(dev);
740 740
741 dev->priv_flags |= IFF_802_1Q_VLAN; 741 dev->priv_flags |= IFF_802_1Q_VLAN;
742 dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
742 dev->tx_queue_len = 0; 743 dev->tx_queue_len = 0;
743 744
744 dev->netdev_ops = &vlan_netdev_ops; 745 dev->netdev_ops = &vlan_netdev_ops;
diff --git a/net/core/dev.c b/net/core/dev.c
index 6d3630d16271..92ebeca29901 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1688,6 +1688,14 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
1688 goto gso; 1688 goto gso;
1689 } 1689 }
1690 1690
1691 /*
1692 * If device doesnt need skb->dst, release it right now while
1693 * its hot in this cpu cache
1694 */
1695 if ((dev->priv_flags & IFF_XMIT_DST_RELEASE) && skb->dst) {
1696 dst_release(skb->dst);
1697 skb->dst = NULL;
1698 }
1691 rc = ops->ndo_start_xmit(skb, dev); 1699 rc = ops->ndo_start_xmit(skb, dev);
1692 /* 1700 /*
1693 * TODO: if skb_orphan() was called by 1701 * TODO: if skb_orphan() was called by
@@ -5045,6 +5053,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
5045 netdev_init_queues(dev); 5053 netdev_init_queues(dev);
5046 5054
5047 INIT_LIST_HEAD(&dev->napi_list); 5055 INIT_LIST_HEAD(&dev->napi_list);
5056 dev->priv_flags = IFF_XMIT_DST_RELEASE;
5048 setup(dev); 5057 setup(dev);
5049 strcpy(dev->name, name); 5058 strcpy(dev->name, name);
5050 return dev; 5059 return dev;