]> git.ipfire.org Git - people/dweismueller/ipfire-2.x.git/blobdiff - src/patches/linux/0006-Drivers-net-hyperv-Cleanup-the-send-path.patch
Merge branch 'hyper-v-fixes' into next
[people/dweismueller/ipfire-2.x.git] / src / patches / linux / 0006-Drivers-net-hyperv-Cleanup-the-send-path.patch
diff --git a/src/patches/linux/0006-Drivers-net-hyperv-Cleanup-the-send-path.patch b/src/patches/linux/0006-Drivers-net-hyperv-Cleanup-the-send-path.patch
new file mode 100644 (file)
index 0000000..20fcb3f
--- /dev/null
@@ -0,0 +1,266 @@
+From d972eb71fb95660fe74616901b55b0d7a336daed Mon Sep 17 00:00:00 2001
+From: KY Srinivasan <kys@microsoft.com>
+Date: Sat, 8 Mar 2014 19:23:14 -0800
+Subject: [PATCH 06/25] Drivers: net: hyperv: Cleanup the send path
+
+In preparation for enabling offloads, cleanup the send path.
+
+Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
+Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/hyperv/hyperv_net.h   |  7 +---
+ drivers/net/hyperv/netvsc_drv.c   | 88 +++++++++++++++++++++++++++++++--------
+ drivers/net/hyperv/rndis_filter.c | 66 -----------------------------
+ 3 files changed, 71 insertions(+), 90 deletions(-)
+
+diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
+index 39fc230f5c20..694bf7cada90 100644
+--- a/drivers/net/hyperv/hyperv_net.h
++++ b/drivers/net/hyperv/hyperv_net.h
+@@ -73,7 +73,7 @@ struct hv_netvsc_packet {
+       } completion;
+       /* This points to the memory after page_buf */
+-      void *extension;
++      struct rndis_message *rndis_msg;
+       u32 total_data_buflen;
+       /* Points to the send/receive buffer where the ethernet frame is */
+@@ -126,11 +126,6 @@ void rndis_filter_device_remove(struct hv_device *dev);
+ int rndis_filter_receive(struct hv_device *dev,
+                       struct hv_netvsc_packet *pkt);
+-
+-
+-int rndis_filter_send(struct hv_device *dev,
+-                      struct hv_netvsc_packet *pkt);
+-
+ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
+ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
+diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
+index 72961741be54..87293a15e470 100644
+--- a/drivers/net/hyperv/netvsc_drv.c
++++ b/drivers/net/hyperv/netvsc_drv.c
+@@ -128,6 +128,27 @@ static int netvsc_close(struct net_device *net)
+       return ret;
+ }
++static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size,
++                              int pkt_type)
++{
++      struct rndis_packet *rndis_pkt;
++      struct rndis_per_packet_info *ppi;
++
++      rndis_pkt = &msg->msg.pkt;
++      rndis_pkt->data_offset += ppi_size;
++
++      ppi = (struct rndis_per_packet_info *)((void *)rndis_pkt +
++              rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_len);
++
++      ppi->size = ppi_size;
++      ppi->type = pkt_type;
++      ppi->ppi_offset = sizeof(struct rndis_per_packet_info);
++
++      rndis_pkt->per_pkt_info_len += ppi_size;
++
++      return ppi;
++}
++
+ static void netvsc_xmit_completion(void *context)
+ {
+       struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
+@@ -174,8 +195,8 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
+       return j + 1;
+ }
+-static void init_page_array(void *hdr, u32 len, struct sk_buff *skb,
+-                          struct hv_page_buffer *pb)
++static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb,
++                         struct hv_page_buffer *pb)
+ {
+       u32 slots_used = 0;
+       char *data = skb->data;
+@@ -203,6 +224,7 @@ static void init_page_array(void *hdr, u32 len, struct sk_buff *skb,
+                                       frag->page_offset,
+                                       skb_frag_size(frag), &pb[slots_used]);
+       }
++      return slots_used;
+ }
+ static int count_skb_frag_slots(struct sk_buff *skb)
+@@ -240,15 +262,20 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
+       struct net_device_context *net_device_ctx = netdev_priv(net);
+       struct hv_netvsc_packet *packet;
+       int ret;
+-      unsigned int num_data_pages;
+       u32 skb_length = skb->len;
++      unsigned int num_data_pgs;
++      struct rndis_message *rndis_msg;
++      struct rndis_packet *rndis_pkt;
++      u32 rndis_msg_size;
++      bool isvlan;
++      struct rndis_per_packet_info *ppi;
+       /* We will atmost need two pages to describe the rndis
+        * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
+        * of pages in a single packet.
+        */
+-      num_data_pages = netvsc_get_slots(skb) + 2;
+-      if (num_data_pages > MAX_PAGE_BUFFER_COUNT) {
++      num_data_pgs = netvsc_get_slots(skb) + 2;
++      if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
+               netdev_err(net, "Packet too big: %u\n", skb->len);
+               dev_kfree_skb(skb);
+               net->stats.tx_dropped++;
+@@ -257,7 +284,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
+       /* Allocate a netvsc packet based on # of frags. */
+       packet = kzalloc(sizeof(struct hv_netvsc_packet) +
+-                       (num_data_pages * sizeof(struct hv_page_buffer)) +
++                       (num_data_pgs * sizeof(struct hv_page_buffer)) +
+                        sizeof(struct rndis_message) +
+                        NDIS_VLAN_PPI_SIZE, GFP_ATOMIC);
+       if (!packet) {
+@@ -271,26 +298,51 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
+       packet->vlan_tci = skb->vlan_tci;
+-      packet->extension = (void *)(unsigned long)packet +
+-                      sizeof(struct hv_netvsc_packet) +
+-                      (num_data_pages * sizeof(struct hv_page_buffer));
+-
+-      /* If the rndis msg goes beyond 1 page, we will add 1 later */
+-      packet->page_buf_cnt = num_data_pages - 1;
+-
+-      /* Initialize it from the skb */
++      packet->is_data_pkt = true;
+       packet->total_data_buflen = skb->len;
+-      /* Start filling in the page buffers starting after RNDIS buffer. */
+-      init_page_array(NULL, 0, skb, &packet->page_buf[1]);
++      packet->rndis_msg = (struct rndis_message *)((unsigned long)packet +
++                              sizeof(struct hv_netvsc_packet) +
++                              (num_data_pgs * sizeof(struct hv_page_buffer)));
+       /* Set the completion routine */
+       packet->completion.send.send_completion = netvsc_xmit_completion;
+       packet->completion.send.send_completion_ctx = packet;
+       packet->completion.send.send_completion_tid = (unsigned long)skb;
+-      ret = rndis_filter_send(net_device_ctx->device_ctx,
+-                                packet);
++      isvlan = packet->vlan_tci & VLAN_TAG_PRESENT;
++
++      /* Add the rndis header */
++      rndis_msg = packet->rndis_msg;
++      rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET;
++      rndis_msg->msg_len = packet->total_data_buflen;
++      rndis_pkt = &rndis_msg->msg.pkt;
++      rndis_pkt->data_offset = sizeof(struct rndis_packet);
++      rndis_pkt->data_len = packet->total_data_buflen;
++      rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet);
++
++      rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
++
++      if (isvlan) {
++              struct ndis_pkt_8021q_info *vlan;
++
++              rndis_msg_size += NDIS_VLAN_PPI_SIZE;
++              ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE,
++                                      IEEE_8021Q_INFO);
++              vlan = (struct ndis_pkt_8021q_info *)((void *)ppi +
++                                              ppi->ppi_offset);
++              vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK;
++              vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >>
++                              VLAN_PRIO_SHIFT;
++      }
++
++      /* Start filling in the page buffers with the rndis hdr */
++      rndis_msg->msg_len += rndis_msg_size;
++      packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
++                                      skb, &packet->page_buf[0]);
++
++      ret = netvsc_send(net_device_ctx->device_ctx, packet);
++
+       if (ret == 0) {
+               net->stats.tx_bytes += skb_length;
+               net->stats.tx_packets++;
+diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
+index 6a9f6021f09c..dcbf144ea7da 100644
+--- a/drivers/net/hyperv/rndis_filter.c
++++ b/drivers/net/hyperv/rndis_filter.c
+@@ -910,69 +910,3 @@ int rndis_filter_close(struct hv_device *dev)
+       return rndis_filter_close_device(nvdev->extension);
+ }
+-
+-int rndis_filter_send(struct hv_device *dev,
+-                           struct hv_netvsc_packet *pkt)
+-{
+-      struct rndis_message *rndis_msg;
+-      struct rndis_packet *rndis_pkt;
+-      u32 rndis_msg_size;
+-      bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT;
+-
+-      /* Add the rndis header */
+-      rndis_msg = (struct rndis_message *)pkt->extension;
+-
+-      rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
+-      if (isvlan)
+-              rndis_msg_size += NDIS_VLAN_PPI_SIZE;
+-
+-      rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET;
+-      rndis_msg->msg_len = pkt->total_data_buflen +
+-                                    rndis_msg_size;
+-
+-      rndis_pkt = &rndis_msg->msg.pkt;
+-      rndis_pkt->data_offset = sizeof(struct rndis_packet);
+-      if (isvlan)
+-              rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE;
+-      rndis_pkt->data_len = pkt->total_data_buflen;
+-
+-      if (isvlan) {
+-              struct rndis_per_packet_info *ppi;
+-              struct ndis_pkt_8021q_info *vlan;
+-
+-              rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet);
+-              rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE;
+-
+-              ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt +
+-                      rndis_pkt->per_pkt_info_offset);
+-              ppi->size = NDIS_VLAN_PPI_SIZE;
+-              ppi->type = IEEE_8021Q_INFO;
+-              ppi->ppi_offset = sizeof(struct rndis_per_packet_info);
+-
+-              vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi +
+-                      ppi->ppi_offset);
+-              vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK;
+-              vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+-      }
+-
+-      pkt->is_data_pkt = true;
+-      pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT;
+-      pkt->page_buf[0].offset =
+-                      (unsigned long)rndis_msg & (PAGE_SIZE-1);
+-      pkt->page_buf[0].len = rndis_msg_size;
+-
+-      /* Add one page_buf if the rndis msg goes beyond page boundary */
+-      if (pkt->page_buf[0].offset + rndis_msg_size > PAGE_SIZE) {
+-              int i;
+-              for (i = pkt->page_buf_cnt; i > 1; i--)
+-                      pkt->page_buf[i] = pkt->page_buf[i-1];
+-              pkt->page_buf_cnt++;
+-              pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset;
+-              pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong)
+-                      rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT;
+-              pkt->page_buf[1].offset = 0;
+-              pkt->page_buf[1].len = rndis_msg_size - pkt->page_buf[0].len;
+-      }
+-
+-      return netvsc_send(dev, pkt);
+-}
+-- 
+2.4.3
+