diff options
author | David S. Miller <davem@davemloft.net> | 2008-07-15 06:47:03 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-17 22:21:13 -0400 |
commit | 8f0f2223cc08a5ae9a77f40edfe02e8a9f1abd77 (patch) | |
tree | 48185f0e8c5a0d220e33a98fe4ac9b323e25579e | |
parent | 51cb6db0f5654f08a4a6bfa3888dc36a51c2df3e (diff) |
net: Implement simple sw TX hashing.
It just xor hashes over IPv4/IPv6 addresses and ports of transport.
The only assumption it makes is that skb_network_header() is set
correctly.
With bug fixes from Eric Dumazet.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/core/dev.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 7ca9564d2f44..467bfb325123 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -121,6 +121,9 @@ | |||
121 | #include <linux/ctype.h> | 121 | #include <linux/ctype.h> |
122 | #include <linux/if_arp.h> | 122 | #include <linux/if_arp.h> |
123 | #include <linux/if_vlan.h> | 123 | #include <linux/if_vlan.h> |
124 | #include <linux/ip.h> | ||
125 | #include <linux/ipv6.h> | ||
126 | #include <linux/in.h> | ||
124 | 127 | ||
125 | #include "net-sysfs.h" | 128 | #include "net-sysfs.h" |
126 | 129 | ||
@@ -1665,6 +1668,53 @@ out_kfree_skb: | |||
1665 | * --BLG | 1668 | * --BLG |
1666 | */ | 1669 | */ |
1667 | 1670 | ||
1671 | static u16 simple_tx_hash(struct net_device *dev, struct sk_buff *skb) | ||
1672 | { | ||
1673 | u32 *addr, *ports, hash, ihl; | ||
1674 | u8 ip_proto; | ||
1675 | int alen; | ||
1676 | |||
1677 | switch (skb->protocol) { | ||
1678 | case __constant_htons(ETH_P_IP): | ||
1679 | ip_proto = ip_hdr(skb)->protocol; | ||
1680 | addr = &ip_hdr(skb)->saddr; | ||
1681 | ihl = ip_hdr(skb)->ihl; | ||
1682 | alen = 2; | ||
1683 | break; | ||
1684 | case __constant_htons(ETH_P_IPV6): | ||
1685 | ip_proto = ipv6_hdr(skb)->nexthdr; | ||
1686 | addr = &ipv6_hdr(skb)->saddr.s6_addr32[0]; | ||
1687 | ihl = (40 >> 2); | ||
1688 | alen = 8; | ||
1689 | break; | ||
1690 | default: | ||
1691 | return 0; | ||
1692 | } | ||
1693 | |||
1694 | ports = (u32 *) (skb_network_header(skb) + (ihl * 4)); | ||
1695 | |||
1696 | hash = 0; | ||
1697 | while (alen--) | ||
1698 | hash ^= *addr++; | ||
1699 | |||
1700 | switch (ip_proto) { | ||
1701 | case IPPROTO_TCP: | ||
1702 | case IPPROTO_UDP: | ||
1703 | case IPPROTO_DCCP: | ||
1704 | case IPPROTO_ESP: | ||
1705 | case IPPROTO_AH: | ||
1706 | case IPPROTO_SCTP: | ||
1707 | case IPPROTO_UDPLITE: | ||
1708 | hash ^= *ports; | ||
1709 | break; | ||
1710 | |||
1711 | default: | ||
1712 | break; | ||
1713 | } | ||
1714 | |||
1715 | return hash % dev->real_num_tx_queues; | ||
1716 | } | ||
1717 | |||
1668 | static struct netdev_queue *dev_pick_tx(struct net_device *dev, | 1718 | static struct netdev_queue *dev_pick_tx(struct net_device *dev, |
1669 | struct sk_buff *skb) | 1719 | struct sk_buff *skb) |
1670 | { | 1720 | { |
@@ -1672,6 +1722,8 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev, | |||
1672 | 1722 | ||
1673 | if (dev->select_queue) | 1723 | if (dev->select_queue) |
1674 | queue_index = dev->select_queue(dev, skb); | 1724 | queue_index = dev->select_queue(dev, skb); |
1725 | else if (dev->real_num_tx_queues > 1) | ||
1726 | queue_index = simple_tx_hash(dev, skb); | ||
1675 | 1727 | ||
1676 | skb_set_queue_mapping(skb, queue_index); | 1728 | skb_set_queue_mapping(skb, queue_index); |
1677 | return netdev_get_tx_queue(dev, queue_index); | 1729 | return netdev_get_tx_queue(dev, queue_index); |