aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorVitaly Kuznetsov <vkuznets@redhat.com>2016-01-25 10:00:41 -0500
committerDavid S. Miller <davem@davemloft.net>2016-01-25 13:51:53 -0500
commit757647e10e55c01fb7a9c4356529442e316a7c72 (patch)
tree301b90371e403baf51efa162078be19e074f300c /drivers/net
parent87e57399e9d3606b08b0e1b2dd0f4aaa1d8ba365 (diff)
hv_netvsc: use skb_get_hash() instead of a homegrown implementation
Recent changes to 'struct flow_keys' (e.g commit d34af823ff40 ("net: Add VLAN ID to flow_keys")) introduced a performance regression in netvsc driver. Is problem is, however, not the above mentioned commit but the fact that netvsc_set_hash() function did some assumptions on the struct flow_keys data layout and this is wrong. Get rid of netvsc_set_hash() by switching to skb_get_hash(). This change will also imply switching to Jenkins hash from the currently used Toeplitz but it seems there is no good excuse for Toeplitz to stay. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/hyperv/netvsc_drv.c67
1 files changed, 3 insertions, 64 deletions
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 1c8db9afdcda..1d3a66563bac 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -196,65 +196,6 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size,
196 return ppi; 196 return ppi;
197} 197}
198 198
199union sub_key {
200 u64 k;
201 struct {
202 u8 pad[3];
203 u8 kb;
204 u32 ka;
205 };
206};
207
208/* Toeplitz hash function
209 * data: network byte order
210 * return: host byte order
211 */
212static u32 comp_hash(u8 *key, int klen, void *data, int dlen)
213{
214 union sub_key subk;
215 int k_next = 4;
216 u8 dt;
217 int i, j;
218 u32 ret = 0;
219
220 subk.k = 0;
221 subk.ka = ntohl(*(u32 *)key);
222
223 for (i = 0; i < dlen; i++) {
224 subk.kb = key[k_next];
225 k_next = (k_next + 1) % klen;
226 dt = ((u8 *)data)[i];
227 for (j = 0; j < 8; j++) {
228 if (dt & 0x80)
229 ret ^= subk.ka;
230 dt <<= 1;
231 subk.k <<= 1;
232 }
233 }
234
235 return ret;
236}
237
238static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb)
239{
240 struct flow_keys flow;
241 int data_len;
242
243 if (!skb_flow_dissect_flow_keys(skb, &flow, 0) ||
244 !(flow.basic.n_proto == htons(ETH_P_IP) ||
245 flow.basic.n_proto == htons(ETH_P_IPV6)))
246 return false;
247
248 if (flow.basic.ip_proto == IPPROTO_TCP)
249 data_len = 12;
250 else
251 data_len = 8;
252
253 *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN, &flow, data_len);
254
255 return true;
256}
257
258static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, 199static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
259 void *accel_priv, select_queue_fallback_t fallback) 200 void *accel_priv, select_queue_fallback_t fallback)
260{ 201{
@@ -267,11 +208,9 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
267 if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1) 208 if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1)
268 return 0; 209 return 0;
269 210
270 if (netvsc_set_hash(&hash, skb)) { 211 hash = skb_get_hash(skb);
271 q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % 212 q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] %
272 ndev->real_num_tx_queues; 213 ndev->real_num_tx_queues;
273 skb_set_hash(skb, hash, PKT_HASH_TYPE_L3);
274 }
275 214
276 if (!nvsc_dev->chn_table[q_idx]) 215 if (!nvsc_dev->chn_table[q_idx])
277 q_idx = 0; 216 q_idx = 0;