Commit 28f50eb2 authored by David S. Miller's avatar David S. Miller

Merge branch 'hv_netvsc-TCP-hash-level'

Haiyang Zhang says:

====================
hv_netvsc: support changing TCP hash level

The patch set simplifies the existing hash level switching code for
UDP. It also adds the support for changing TCP hash level. So users
can switch between L3 an L4 hash levels for TCP and UDP.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c1b85a19 78005d91
...@@ -19,12 +19,12 @@ Features ...@@ -19,12 +19,12 @@ Features
Receive Side Scaling Receive Side Scaling
-------------------- --------------------
Hyper-V supports receive side scaling. For TCP, packets are Hyper-V supports receive side scaling. For TCP & UDP, packets can
distributed among available queues based on IP address and port be distributed among available queues based on IP address and port
number. number.
For UDP, we can switch UDP hash level between L3 and L4 by ethtool For TCP & UDP, we can switch hash level between L3 and L4 by ethtool
command. UDP over IPv4 and v6 can be set differently. The default command. TCP/UDP over IPv4 and v6 can be set differently. The default
hash level is L4. We currently only allow switching TX hash level hash level is L4. We currently only allow switching TX hash level
from within the guests. from within the guests.
......
...@@ -704,6 +704,14 @@ struct netvsc_reconfig { ...@@ -704,6 +704,14 @@ struct netvsc_reconfig {
u32 event; u32 event;
}; };
/* L4 hash bits for different protocols */
#define HV_TCP4_L4HASH 1
#define HV_TCP6_L4HASH 2
#define HV_UDP4_L4HASH 4
#define HV_UDP6_L4HASH 8
#define HV_DEFAULT_L4HASH (HV_TCP4_L4HASH | HV_TCP6_L4HASH | HV_UDP4_L4HASH | \
HV_UDP6_L4HASH)
/* The context of the netvsc device */ /* The context of the netvsc device */
struct net_device_context { struct net_device_context {
/* point back to our device context */ /* point back to our device context */
...@@ -726,10 +734,9 @@ struct net_device_context { ...@@ -726,10 +734,9 @@ struct net_device_context {
u32 tx_send_table[VRSS_SEND_TAB_SIZE]; u32 tx_send_table[VRSS_SEND_TAB_SIZE];
/* Ethtool settings */ /* Ethtool settings */
bool udp4_l4_hash;
bool udp6_l4_hash;
u8 duplex; u8 duplex;
u32 speed; u32 speed;
u32 l4_hash; /* L4 hash settings */
struct netvsc_ethtool_stats eth_stats; struct netvsc_ethtool_stats eth_stats;
/* State to manage the associated VF interface. */ /* State to manage the associated VF interface. */
......
...@@ -203,7 +203,7 @@ static inline u32 netvsc_get_hash( ...@@ -203,7 +203,7 @@ static inline u32 netvsc_get_hash(
const struct net_device_context *ndc) const struct net_device_context *ndc)
{ {
struct flow_keys flow; struct flow_keys flow;
u32 hash; u32 hash, pkt_proto = 0;
static u32 hashrnd __read_mostly; static u32 hashrnd __read_mostly;
net_get_random_once(&hashrnd, sizeof(hashrnd)); net_get_random_once(&hashrnd, sizeof(hashrnd));
...@@ -211,11 +211,25 @@ static inline u32 netvsc_get_hash( ...@@ -211,11 +211,25 @@ static inline u32 netvsc_get_hash(
if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) if (!skb_flow_dissect_flow_keys(skb, &flow, 0))
return 0; return 0;
if (flow.basic.ip_proto == IPPROTO_TCP || switch (flow.basic.ip_proto) {
(flow.basic.ip_proto == IPPROTO_UDP && case IPPROTO_TCP:
((flow.basic.n_proto == htons(ETH_P_IP) && ndc->udp4_l4_hash) || if (flow.basic.n_proto == htons(ETH_P_IP))
(flow.basic.n_proto == htons(ETH_P_IPV6) && pkt_proto = HV_TCP4_L4HASH;
ndc->udp6_l4_hash)))) { else if (flow.basic.n_proto == htons(ETH_P_IPV6))
pkt_proto = HV_TCP6_L4HASH;
break;
case IPPROTO_UDP:
if (flow.basic.n_proto == htons(ETH_P_IP))
pkt_proto = HV_UDP4_L4HASH;
else if (flow.basic.n_proto == htons(ETH_P_IPV6))
pkt_proto = HV_UDP6_L4HASH;
break;
}
if (pkt_proto & ndc->l4_hash) {
return skb_get_hash(skb); return skb_get_hash(skb);
} else { } else {
if (flow.basic.n_proto == htons(ETH_P_IP)) if (flow.basic.n_proto == htons(ETH_P_IP))
...@@ -898,8 +912,7 @@ static void netvsc_init_settings(struct net_device *dev) ...@@ -898,8 +912,7 @@ static void netvsc_init_settings(struct net_device *dev)
{ {
struct net_device_context *ndc = netdev_priv(dev); struct net_device_context *ndc = netdev_priv(dev);
ndc->udp4_l4_hash = true; ndc->l4_hash = HV_DEFAULT_L4HASH;
ndc->udp6_l4_hash = true;
ndc->speed = SPEED_UNKNOWN; ndc->speed = SPEED_UNKNOWN;
ndc->duplex = DUPLEX_FULL; ndc->duplex = DUPLEX_FULL;
...@@ -1245,23 +1258,32 @@ static int ...@@ -1245,23 +1258,32 @@ static int
netvsc_get_rss_hash_opts(struct net_device_context *ndc, netvsc_get_rss_hash_opts(struct net_device_context *ndc,
struct ethtool_rxnfc *info) struct ethtool_rxnfc *info)
{ {
const u32 l4_flag = RXH_L4_B_0_1 | RXH_L4_B_2_3;
info->data = RXH_IP_SRC | RXH_IP_DST; info->data = RXH_IP_SRC | RXH_IP_DST;
switch (info->flow_type) { switch (info->flow_type) {
case TCP_V4_FLOW: case TCP_V4_FLOW:
if (ndc->l4_hash & HV_TCP4_L4HASH)
info->data |= l4_flag;
break;
case TCP_V6_FLOW: case TCP_V6_FLOW:
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; if (ndc->l4_hash & HV_TCP6_L4HASH)
info->data |= l4_flag;
break; break;
case UDP_V4_FLOW: case UDP_V4_FLOW:
if (ndc->udp4_l4_hash) if (ndc->l4_hash & HV_UDP4_L4HASH)
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; info->data |= l4_flag;
break; break;
case UDP_V6_FLOW: case UDP_V6_FLOW:
if (ndc->udp6_l4_hash) if (ndc->l4_hash & HV_UDP6_L4HASH)
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; info->data |= l4_flag;
break; break;
...@@ -1302,23 +1324,51 @@ static int netvsc_set_rss_hash_opts(struct net_device_context *ndc, ...@@ -1302,23 +1324,51 @@ static int netvsc_set_rss_hash_opts(struct net_device_context *ndc,
{ {
if (info->data == (RXH_IP_SRC | RXH_IP_DST | if (info->data == (RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) { RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
if (info->flow_type == UDP_V4_FLOW) switch (info->flow_type) {
ndc->udp4_l4_hash = true; case TCP_V4_FLOW:
else if (info->flow_type == UDP_V6_FLOW) ndc->l4_hash |= HV_TCP4_L4HASH;
ndc->udp6_l4_hash = true; break;
else
case TCP_V6_FLOW:
ndc->l4_hash |= HV_TCP6_L4HASH;
break;
case UDP_V4_FLOW:
ndc->l4_hash |= HV_UDP4_L4HASH;
break;
case UDP_V6_FLOW:
ndc->l4_hash |= HV_UDP6_L4HASH;
break;
default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
return 0; return 0;
} }
if (info->data == (RXH_IP_SRC | RXH_IP_DST)) { if (info->data == (RXH_IP_SRC | RXH_IP_DST)) {
if (info->flow_type == UDP_V4_FLOW) switch (info->flow_type) {
ndc->udp4_l4_hash = false; case TCP_V4_FLOW:
else if (info->flow_type == UDP_V6_FLOW) ndc->l4_hash &= ~HV_TCP4_L4HASH;
ndc->udp6_l4_hash = false; break;
else
case TCP_V6_FLOW:
ndc->l4_hash &= ~HV_TCP6_L4HASH;
break;
case UDP_V4_FLOW:
ndc->l4_hash &= ~HV_UDP4_L4HASH;
break;
case UDP_V6_FLOW:
ndc->l4_hash &= ~HV_UDP6_L4HASH;
break;
default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment