aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorKY Srinivasan <kys@microsoft.com>2015-04-28 20:59:48 -0400
committerDavid S. Miller <davem@davemloft.net>2015-04-29 15:20:39 -0400
commitb56fc3c536541c8081cd5f1f1d101a16c002a365 (patch)
tree4d887b0f261c23862d847c435c7bf94b671ad91c /drivers/net
parent48734994ac268eb391a66dca4cde7d08a08aba08 (diff)
hv_netvsc: Fix a bug in netvsc_start_xmit()
Commit b08cc79155fc26d0d112b1470d1ece5034651a4b eliminated memory allocation in the packet send path: "hv_netvsc: Eliminate memory allocation in the packet send path The network protocol used to communicate with the host is the remote ndis (rndis) protocol. We need to decorate each outgoing packet with a rndis header and additional rndis state (rndis per-packet state). To manage this state, we currently allocate memory in the transmit path. Eliminate this allocation by requesting additional head room in the skb." This commit introduced a bug since it did not account for the case if the skb was cloned. Fix this bug. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Tested-by: Dexuan Cui <decui@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/hyperv/hyperv_net.h1
-rw-r--r--drivers/net/hyperv/netvsc.c5
-rw-r--r--drivers/net/hyperv/netvsc_drv.c27
3 files changed, 7 insertions, 26 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index e55c8f4f55ef..41071d32bc8e 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -128,7 +128,6 @@ struct ndis_tcp_ip_checksum_info;
128struct hv_netvsc_packet { 128struct hv_netvsc_packet {
129 /* Bookkeeping stuff */ 129 /* Bookkeeping stuff */
130 u32 status; 130 u32 status;
131 bool part_of_skb;
132 131
133 bool is_data_pkt; 132 bool is_data_pkt;
134 bool xmit_more; /* from skb */ 133 bool xmit_more; /* from skb */
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index c651d4d8b747..2d9ef533cc48 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -889,11 +889,6 @@ int netvsc_send(struct hv_device *device,
889 } else { 889 } else {
890 packet->page_buf_cnt = 0; 890 packet->page_buf_cnt = 0;
891 packet->total_data_buflen += msd_len; 891 packet->total_data_buflen += msd_len;
892 if (!packet->part_of_skb) {
893 skb = (struct sk_buff *)(unsigned long)packet->
894 send_completion_tid;
895 packet->send_completion_tid = 0;
896 }
897 } 892 }
898 893
899 if (msdp->pkt) 894 if (msdp->pkt)
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 66c4b0c8d108..5993c7e2d723 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -238,9 +238,6 @@ void netvsc_xmit_completion(void *context)
238 struct sk_buff *skb = (struct sk_buff *) 238 struct sk_buff *skb = (struct sk_buff *)
239 (unsigned long)packet->send_completion_tid; 239 (unsigned long)packet->send_completion_tid;
240 240
241 if (!packet->part_of_skb)
242 kfree(packet);
243
244 if (skb) 241 if (skb)
245 dev_kfree_skb_any(skb); 242 dev_kfree_skb_any(skb);
246} 243}
@@ -392,7 +389,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
392 u32 net_trans_info; 389 u32 net_trans_info;
393 u32 hash; 390 u32 hash;
394 u32 skb_length; 391 u32 skb_length;
395 u32 head_room;
396 u32 pkt_sz; 392 u32 pkt_sz;
397 struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; 393 struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
398 394
@@ -405,7 +401,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
405 401
406check_size: 402check_size:
407 skb_length = skb->len; 403 skb_length = skb->len;
408 head_room = skb_headroom(skb);
409 num_data_pgs = netvsc_get_slots(skb) + 2; 404 num_data_pgs = netvsc_get_slots(skb) + 2;
410 if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) { 405 if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) {
411 net_alert_ratelimited("packet too big: %u pages (%u bytes)\n", 406 net_alert_ratelimited("packet too big: %u pages (%u bytes)\n",
@@ -424,20 +419,14 @@ check_size:
424 419
425 pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE; 420 pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;
426 421
427 if (head_room < pkt_sz) { 422 ret = skb_cow_head(skb, pkt_sz);
428 packet = kmalloc(pkt_sz, GFP_ATOMIC); 423 if (ret) {
429 if (!packet) { 424 netdev_err(net, "unable to alloc hv_netvsc_packet\n");
430 /* out of memory, drop packet */ 425 ret = -ENOMEM;
431 netdev_err(net, "unable to alloc hv_netvsc_packet\n"); 426 goto drop;
432 ret = -ENOMEM;
433 goto drop;
434 }
435 packet->part_of_skb = false;
436 } else {
437 /* Use the headroom for building up the packet */
438 packet = (struct hv_netvsc_packet *)skb->head;
439 packet->part_of_skb = true;
440 } 427 }
428 /* Use the headroom for building up the packet */
429 packet = (struct hv_netvsc_packet *)skb->head;
441 430
442 packet->status = 0; 431 packet->status = 0;
443 packet->xmit_more = skb->xmit_more; 432 packet->xmit_more = skb->xmit_more;
@@ -594,8 +583,6 @@ drop:
594 net->stats.tx_bytes += skb_length; 583 net->stats.tx_bytes += skb_length;
595 net->stats.tx_packets++; 584 net->stats.tx_packets++;
596 } else { 585 } else {
597 if (packet && !packet->part_of_skb)
598 kfree(packet);
599 if (ret != -EAGAIN) { 586 if (ret != -EAGAIN) {
600 dev_kfree_skb_any(skb); 587 dev_kfree_skb_any(skb);
601 net->stats.tx_dropped++; 588 net->stats.tx_dropped++;