diff options
| author | David S. Miller <davem@davemloft.net> | 2014-03-10 15:52:17 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-03-10 15:52:17 -0400 |
| commit | e3ca64948b50d05304a33ad8c4332e2a748e9401 (patch) | |
| tree | 5420a2dc396c09173940c73d761f0aea0fd6bed5 | |
| parent | 3ee2f8ce1ab8f235bda164295fa0cf39ec1c2400 (diff) | |
| parent | 77bf5487946254798ed7f265877939c703189f1e (diff) | |
Merge branch 'hyperv-next'
K. Y. Srinivasan says:
====================
Drivers: net: hyperv: Enable various offloads
This patch set enables both checksum as well as segmentation offload.
As part of this effort I have enabled scatter gather I/O a well.
In version 2 of these patches, I addressed comments from David Miller and
Dan Carpenter.
In this version I have addressed the latest comments from David Miller.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 145 | ||||
| -rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 333 | ||||
| -rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 150 |
3 files changed, 504 insertions, 124 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 39fc230f5c20..7d06b4959383 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | 30 | ||
| 31 | /* Fwd declaration */ | 31 | /* Fwd declaration */ |
| 32 | struct hv_netvsc_packet; | 32 | struct hv_netvsc_packet; |
| 33 | struct ndis_tcp_ip_checksum_info; | ||
| 33 | 34 | ||
| 34 | /* Represent the xfer page packet which contains 1 or more netvsc packet */ | 35 | /* Represent the xfer page packet which contains 1 or more netvsc packet */ |
| 35 | struct xferpage_packet { | 36 | struct xferpage_packet { |
| @@ -73,7 +74,7 @@ struct hv_netvsc_packet { | |||
| 73 | } completion; | 74 | } completion; |
| 74 | 75 | ||
| 75 | /* This points to the memory after page_buf */ | 76 | /* This points to the memory after page_buf */ |
| 76 | void *extension; | 77 | struct rndis_message *rndis_msg; |
| 77 | 78 | ||
| 78 | u32 total_data_buflen; | 79 | u32 total_data_buflen; |
| 79 | /* Points to the send/receive buffer where the ethernet frame is */ | 80 | /* Points to the send/receive buffer where the ethernet frame is */ |
| @@ -117,7 +118,8 @@ int netvsc_send(struct hv_device *device, | |||
| 117 | void netvsc_linkstatus_callback(struct hv_device *device_obj, | 118 | void netvsc_linkstatus_callback(struct hv_device *device_obj, |
| 118 | unsigned int status); | 119 | unsigned int status); |
| 119 | int netvsc_recv_callback(struct hv_device *device_obj, | 120 | int netvsc_recv_callback(struct hv_device *device_obj, |
| 120 | struct hv_netvsc_packet *packet); | 121 | struct hv_netvsc_packet *packet, |
| 122 | struct ndis_tcp_ip_checksum_info *csum_info); | ||
| 121 | int rndis_filter_open(struct hv_device *dev); | 123 | int rndis_filter_open(struct hv_device *dev); |
| 122 | int rndis_filter_close(struct hv_device *dev); | 124 | int rndis_filter_close(struct hv_device *dev); |
| 123 | int rndis_filter_device_add(struct hv_device *dev, | 125 | int rndis_filter_device_add(struct hv_device *dev, |
| @@ -126,11 +128,6 @@ void rndis_filter_device_remove(struct hv_device *dev); | |||
| 126 | int rndis_filter_receive(struct hv_device *dev, | 128 | int rndis_filter_receive(struct hv_device *dev, |
| 127 | struct hv_netvsc_packet *pkt); | 129 | struct hv_netvsc_packet *pkt); |
| 128 | 130 | ||
| 129 | |||
| 130 | |||
| 131 | int rndis_filter_send(struct hv_device *dev, | ||
| 132 | struct hv_netvsc_packet *pkt); | ||
| 133 | |||
| 134 | int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); | 131 | int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); |
| 135 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); | 132 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); |
| 136 | 133 | ||
| @@ -726,9 +723,133 @@ struct ndis_pkt_8021q_info { | |||
| 726 | }; | 723 | }; |
| 727 | }; | 724 | }; |
| 728 | 725 | ||
| 726 | struct ndis_oject_header { | ||
| 727 | u8 type; | ||
| 728 | u8 revision; | ||
| 729 | u16 size; | ||
| 730 | }; | ||
| 731 | |||
| 732 | #define NDIS_OBJECT_TYPE_DEFAULT 0x80 | ||
| 733 | #define NDIS_OFFLOAD_PARAMETERS_REVISION_3 3 | ||
| 734 | #define NDIS_OFFLOAD_PARAMETERS_NO_CHANGE 0 | ||
| 735 | #define NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED 1 | ||
| 736 | #define NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED 2 | ||
| 737 | #define NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED 2 | ||
| 738 | #define NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED 1 | ||
| 739 | #define NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED 2 | ||
| 740 | #define NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED 1 | ||
| 741 | #define NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED 2 | ||
| 742 | #define NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED 3 | ||
| 743 | #define NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED 4 | ||
| 744 | |||
| 745 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE 1 | ||
| 746 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0 | ||
| 747 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1 | ||
| 748 | |||
| 749 | /* | ||
| 750 | * New offload OIDs for NDIS 6 | ||
| 751 | */ | ||
| 752 | #define OID_TCP_OFFLOAD_CURRENT_CONFIG 0xFC01020B /* query only */ | ||
| 753 | #define OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C /* set only */ | ||
| 754 | #define OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020D/* query only */ | ||
| 755 | #define OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG 0xFC01020E /* query only */ | ||
| 756 | #define OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020F /* query */ | ||
| 757 | #define OID_OFFLOAD_ENCAPSULATION 0x0101010A /* set/query */ | ||
| 758 | |||
| 759 | struct ndis_offload_params { | ||
| 760 | struct ndis_oject_header header; | ||
| 761 | u8 ip_v4_csum; | ||
| 762 | u8 tcp_ip_v4_csum; | ||
| 763 | u8 udp_ip_v4_csum; | ||
| 764 | u8 tcp_ip_v6_csum; | ||
| 765 | u8 udp_ip_v6_csum; | ||
| 766 | u8 lso_v1; | ||
| 767 | u8 ip_sec_v1; | ||
| 768 | u8 lso_v2_ipv4; | ||
| 769 | u8 lso_v2_ipv6; | ||
| 770 | u8 tcp_connection_ip_v4; | ||
| 771 | u8 tcp_connection_ip_v6; | ||
| 772 | u32 flags; | ||
| 773 | u8 ip_sec_v2; | ||
| 774 | u8 ip_sec_v2_ip_v4; | ||
| 775 | struct { | ||
| 776 | u8 rsc_ip_v4; | ||
| 777 | u8 rsc_ip_v6; | ||
| 778 | }; | ||
| 779 | struct { | ||
| 780 | u8 encapsulated_packet_task_offload; | ||
| 781 | u8 encapsulation_types; | ||
| 782 | }; | ||
| 783 | }; | ||
| 784 | |||
| 785 | struct ndis_tcp_ip_checksum_info { | ||
| 786 | union { | ||
| 787 | struct { | ||
| 788 | u32 is_ipv4:1; | ||
| 789 | u32 is_ipv6:1; | ||
| 790 | u32 tcp_checksum:1; | ||
| 791 | u32 udp_checksum:1; | ||
| 792 | u32 ip_header_checksum:1; | ||
| 793 | u32 reserved:11; | ||
| 794 | u32 tcp_header_offset:10; | ||
| 795 | } transmit; | ||
| 796 | struct { | ||
| 797 | u32 tcp_checksum_failed:1; | ||
| 798 | u32 udp_checksum_failed:1; | ||
| 799 | u32 ip_checksum_failed:1; | ||
| 800 | u32 tcp_checksum_succeeded:1; | ||
| 801 | u32 udp_checksum_succeeded:1; | ||
| 802 | u32 ip_checksum_succeeded:1; | ||
| 803 | u32 loopback:1; | ||
| 804 | u32 tcp_checksum_value_invalid:1; | ||
| 805 | u32 ip_checksum_value_invalid:1; | ||
| 806 | } receive; | ||
| 807 | u32 value; | ||
| 808 | }; | ||
| 809 | }; | ||
| 810 | |||
| 811 | struct ndis_tcp_lso_info { | ||
| 812 | union { | ||
| 813 | struct { | ||
| 814 | u32 unused:30; | ||
| 815 | u32 type:1; | ||
| 816 | u32 reserved2:1; | ||
| 817 | } transmit; | ||
| 818 | struct { | ||
| 819 | u32 mss:20; | ||
| 820 | u32 tcp_header_offset:10; | ||
| 821 | u32 type:1; | ||
| 822 | u32 reserved2:1; | ||
| 823 | } lso_v1_transmit; | ||
| 824 | struct { | ||
| 825 | u32 tcp_payload:30; | ||
| 826 | u32 type:1; | ||
| 827 | u32 reserved2:1; | ||
| 828 | } lso_v1_transmit_complete; | ||
| 829 | struct { | ||
| 830 | u32 mss:20; | ||
| 831 | u32 tcp_header_offset:10; | ||
| 832 | u32 type:1; | ||
| 833 | u32 ip_version:1; | ||
| 834 | } lso_v2_transmit; | ||
| 835 | struct { | ||
| 836 | u32 reserved:30; | ||
| 837 | u32 type:1; | ||
| 838 | u32 reserved2:1; | ||
| 839 | } lso_v2_transmit_complete; | ||
| 840 | u32 value; | ||
| 841 | }; | ||
| 842 | }; | ||
| 843 | |||
| 729 | #define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | 844 | #define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ |
| 730 | sizeof(struct ndis_pkt_8021q_info)) | 845 | sizeof(struct ndis_pkt_8021q_info)) |
| 731 | 846 | ||
| 847 | #define NDIS_CSUM_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | ||
| 848 | sizeof(struct ndis_tcp_ip_checksum_info)) | ||
| 849 | |||
| 850 | #define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | ||
| 851 | sizeof(struct ndis_tcp_lso_info)) | ||
| 852 | |||
| 732 | /* Format of Information buffer passed in a SetRequest for the OID */ | 853 | /* Format of Information buffer passed in a SetRequest for the OID */ |
| 733 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ | 854 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ |
| 734 | struct rndis_config_parameter_info { | 855 | struct rndis_config_parameter_info { |
| @@ -954,6 +1075,16 @@ struct rndis_message { | |||
| 954 | #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 | 1075 | #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 |
| 955 | #define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 | 1076 | #define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 |
| 956 | 1077 | ||
| 1078 | #define INFO_IPV4 2 | ||
| 1079 | #define INFO_IPV6 4 | ||
| 1080 | #define INFO_TCP 2 | ||
| 1081 | #define INFO_UDP 4 | ||
| 1082 | |||
| 1083 | #define TRANSPORT_INFO_NOT_IP 0 | ||
| 1084 | #define TRANSPORT_INFO_IPV4_TCP ((INFO_IPV4 << 16) | INFO_TCP) | ||
| 1085 | #define TRANSPORT_INFO_IPV4_UDP ((INFO_IPV4 << 16) | INFO_UDP) | ||
| 1086 | #define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP) | ||
| 1087 | #define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP) | ||
| 957 | 1088 | ||
| 958 | 1089 | ||
| 959 | #endif /* _HYPERV_NET_H */ | 1090 | #endif /* _HYPERV_NET_H */ |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 9ef6be90a81c..3d069901e6d9 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) | |||
| 128 | return ret; | 128 | return ret; |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, | ||
| 132 | int pkt_type) | ||
| 133 | { | ||
| 134 | struct rndis_packet *rndis_pkt; | ||
| 135 | struct rndis_per_packet_info *ppi; | ||
| 136 | |||
| 137 | rndis_pkt = &msg->msg.pkt; | ||
| 138 | rndis_pkt->data_offset += ppi_size; | ||
| 139 | |||
| 140 | ppi = (struct rndis_per_packet_info *)((void *)rndis_pkt + | ||
| 141 | rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_len); | ||
| 142 | |||
| 143 | ppi->size = ppi_size; | ||
| 144 | ppi->type = pkt_type; | ||
| 145 | ppi->ppi_offset = sizeof(struct rndis_per_packet_info); | ||
| 146 | |||
| 147 | rndis_pkt->per_pkt_info_len += ppi_size; | ||
| 148 | |||
| 149 | return ppi; | ||
| 150 | } | ||
| 151 | |||
| 131 | static void netvsc_xmit_completion(void *context) | 152 | static void netvsc_xmit_completion(void *context) |
| 132 | { | 153 | { |
| 133 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; | 154 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; |
| @@ -140,21 +161,163 @@ static void netvsc_xmit_completion(void *context) | |||
| 140 | dev_kfree_skb_any(skb); | 161 | dev_kfree_skb_any(skb); |
| 141 | } | 162 | } |
| 142 | 163 | ||
| 164 | static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, | ||
| 165 | struct hv_page_buffer *pb) | ||
| 166 | { | ||
| 167 | int j = 0; | ||
| 168 | |||
| 169 | /* Deal with compund pages by ignoring unused part | ||
| 170 | * of the page. | ||
| 171 | */ | ||
| 172 | page += (offset >> PAGE_SHIFT); | ||
| 173 | offset &= ~PAGE_MASK; | ||
| 174 | |||
| 175 | while (len > 0) { | ||
| 176 | unsigned long bytes; | ||
| 177 | |||
| 178 | bytes = PAGE_SIZE - offset; | ||
| 179 | if (bytes > len) | ||
| 180 | bytes = len; | ||
| 181 | pb[j].pfn = page_to_pfn(page); | ||
| 182 | pb[j].offset = offset; | ||
| 183 | pb[j].len = bytes; | ||
| 184 | |||
| 185 | offset += bytes; | ||
| 186 | len -= bytes; | ||
| 187 | |||
| 188 | if (offset == PAGE_SIZE && len) { | ||
| 189 | page++; | ||
| 190 | offset = 0; | ||
| 191 | j++; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | return j + 1; | ||
| 196 | } | ||
| 197 | |||
| 198 | static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, | ||
| 199 | struct hv_page_buffer *pb) | ||
| 200 | { | ||
| 201 | u32 slots_used = 0; | ||
| 202 | char *data = skb->data; | ||
| 203 | int frags = skb_shinfo(skb)->nr_frags; | ||
| 204 | int i; | ||
| 205 | |||
| 206 | /* The packet is laid out thus: | ||
| 207 | * 1. hdr | ||
| 208 | * 2. skb linear data | ||
| 209 | * 3. skb fragment data | ||
| 210 | */ | ||
| 211 | if (hdr != NULL) | ||
| 212 | slots_used += fill_pg_buf(virt_to_page(hdr), | ||
| 213 | offset_in_page(hdr), | ||
| 214 | len, &pb[slots_used]); | ||
| 215 | |||
| 216 | slots_used += fill_pg_buf(virt_to_page(data), | ||
| 217 | offset_in_page(data), | ||
| 218 | skb_headlen(skb), &pb[slots_used]); | ||
| 219 | |||
| 220 | for (i = 0; i < frags; i++) { | ||
| 221 | skb_frag_t *frag = skb_shinfo(skb)->frags + i; | ||
| 222 | |||
| 223 | slots_used += fill_pg_buf(skb_frag_page(frag), | ||
| 224 | frag->page_offset, | ||
| 225 | skb_frag_size(frag), &pb[slots_used]); | ||
| 226 | } | ||
| 227 | return slots_used; | ||
| 228 | } | ||
| 229 | |||
| 230 | static int count_skb_frag_slots(struct sk_buff *skb) | ||
| 231 | { | ||
| 232 | int i, frags = skb_shinfo(skb)->nr_frags; | ||
| 233 | int pages = 0; | ||
| 234 | |||
| 235 | for (i = 0; i < frags; i++) { | ||
| 236 | skb_frag_t *frag = skb_shinfo(skb)->frags + i; | ||
| 237 | unsigned long size = skb_frag_size(frag); | ||
| 238 | unsigned long offset = frag->page_offset; | ||
| 239 | |||
| 240 | /* Skip unused frames from start of page */ | ||
| 241 | offset &= ~PAGE_MASK; | ||
| 242 | pages += PFN_UP(offset + size); | ||
| 243 | } | ||
| 244 | return pages; | ||
| 245 | } | ||
| 246 | |||
| 247 | static int netvsc_get_slots(struct sk_buff *skb) | ||
| 248 | { | ||
| 249 | char *data = skb->data; | ||
| 250 | unsigned int offset = offset_in_page(data); | ||
| 251 | unsigned int len = skb_headlen(skb); | ||
| 252 | int slots; | ||
| 253 | int frag_slots; | ||
| 254 | |||
| 255 | slots = DIV_ROUND_UP(offset + len, PAGE_SIZE); | ||
| 256 | frag_slots = count_skb_frag_slots(skb); | ||
| 257 | return slots + frag_slots; | ||
| 258 | } | ||
| 259 | |||
| 260 | static u32 get_net_transport_info(struct sk_buff *skb, u32 *trans_off) | ||
| 261 | { | ||
| 262 | u32 ret_val = TRANSPORT_INFO_NOT_IP; | ||
| 263 | |||
| 264 | if ((eth_hdr(skb)->h_proto != htons(ETH_P_IP)) && | ||
| 265 | (eth_hdr(skb)->h_proto != htons(ETH_P_IPV6))) { | ||
| 266 | goto not_ip; | ||
| 267 | } | ||
| 268 | |||
| 269 | *trans_off = skb_transport_offset(skb); | ||
| 270 | |||
| 271 | if ((eth_hdr(skb)->h_proto == htons(ETH_P_IP))) { | ||
| 272 | struct iphdr *iphdr = ip_hdr(skb); | ||
| 273 | |||
| 274 | if (iphdr->protocol == IPPROTO_TCP) | ||
| 275 | ret_val = TRANSPORT_INFO_IPV4_TCP; | ||
| 276 | else if (iphdr->protocol == IPPROTO_UDP) | ||
| 277 | ret_val = TRANSPORT_INFO_IPV4_UDP; | ||
| 278 | } else { | ||
| 279 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) | ||
| 280 | ret_val = TRANSPORT_INFO_IPV6_TCP; | ||
| 281 | else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) | ||
| 282 | ret_val = TRANSPORT_INFO_IPV6_UDP; | ||
| 283 | } | ||
| 284 | |||
| 285 | not_ip: | ||
| 286 | return ret_val; | ||
| 287 | } | ||
| 288 | |||
| 143 | static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | 289 | static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) |
| 144 | { | 290 | { |
| 145 | struct net_device_context *net_device_ctx = netdev_priv(net); | 291 | struct net_device_context *net_device_ctx = netdev_priv(net); |
| 146 | struct hv_netvsc_packet *packet; | 292 | struct hv_netvsc_packet *packet; |
| 147 | int ret; | 293 | int ret; |
| 148 | unsigned int i, num_pages, npg_data; | 294 | unsigned int num_data_pgs; |
| 149 | 295 | struct rndis_message *rndis_msg; | |
| 150 | /* Add multipages for skb->data and additional 2 for RNDIS */ | 296 | struct rndis_packet *rndis_pkt; |
| 151 | npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) | 297 | u32 rndis_msg_size; |
| 152 | >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; | 298 | bool isvlan; |
| 153 | num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; | 299 | struct rndis_per_packet_info *ppi; |
| 300 | struct ndis_tcp_ip_checksum_info *csum_info; | ||
| 301 | struct ndis_tcp_lso_info *lso_info; | ||
| 302 | int hdr_offset; | ||
| 303 | u32 net_trans_info; | ||
| 304 | |||
| 305 | |||
| 306 | /* We will atmost need two pages to describe the rndis | ||
| 307 | * header. We can only transmit MAX_PAGE_BUFFER_COUNT number | ||
| 308 | * of pages in a single packet. | ||
| 309 | */ | ||
| 310 | num_data_pgs = netvsc_get_slots(skb) + 2; | ||
| 311 | if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { | ||
| 312 | netdev_err(net, "Packet too big: %u\n", skb->len); | ||
| 313 | dev_kfree_skb(skb); | ||
| 314 | net->stats.tx_dropped++; | ||
| 315 | return NETDEV_TX_OK; | ||
| 316 | } | ||
| 154 | 317 | ||
| 155 | /* Allocate a netvsc packet based on # of frags. */ | 318 | /* Allocate a netvsc packet based on # of frags. */ |
| 156 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | 319 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + |
| 157 | (num_pages * sizeof(struct hv_page_buffer)) + | 320 | (num_data_pgs * sizeof(struct hv_page_buffer)) + |
| 158 | sizeof(struct rndis_message) + | 321 | sizeof(struct rndis_message) + |
| 159 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); | 322 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); |
| 160 | if (!packet) { | 323 | if (!packet) { |
| @@ -168,53 +331,111 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
| 168 | 331 | ||
| 169 | packet->vlan_tci = skb->vlan_tci; | 332 | packet->vlan_tci = skb->vlan_tci; |
| 170 | 333 | ||
| 171 | packet->extension = (void *)(unsigned long)packet + | 334 | packet->is_data_pkt = true; |
| 335 | packet->total_data_buflen = skb->len; | ||
| 336 | |||
| 337 | packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + | ||
| 172 | sizeof(struct hv_netvsc_packet) + | 338 | sizeof(struct hv_netvsc_packet) + |
| 173 | (num_pages * sizeof(struct hv_page_buffer)); | 339 | (num_data_pgs * sizeof(struct hv_page_buffer))); |
| 340 | |||
| 341 | /* Set the completion routine */ | ||
| 342 | packet->completion.send.send_completion = netvsc_xmit_completion; | ||
| 343 | packet->completion.send.send_completion_ctx = packet; | ||
| 344 | packet->completion.send.send_completion_tid = (unsigned long)skb; | ||
| 174 | 345 | ||
| 175 | /* If the rndis msg goes beyond 1 page, we will add 1 later */ | 346 | isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; |
| 176 | packet->page_buf_cnt = num_pages - 1; | 347 | |
| 348 | /* Add the rndis header */ | ||
| 349 | rndis_msg = packet->rndis_msg; | ||
| 350 | rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; | ||
| 351 | rndis_msg->msg_len = packet->total_data_buflen; | ||
| 352 | rndis_pkt = &rndis_msg->msg.pkt; | ||
| 353 | rndis_pkt->data_offset = sizeof(struct rndis_packet); | ||
| 354 | rndis_pkt->data_len = packet->total_data_buflen; | ||
| 355 | rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); | ||
| 356 | |||
| 357 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); | ||
| 358 | |||
| 359 | if (isvlan) { | ||
| 360 | struct ndis_pkt_8021q_info *vlan; | ||
| 361 | |||
| 362 | rndis_msg_size += NDIS_VLAN_PPI_SIZE; | ||
| 363 | ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE, | ||
| 364 | IEEE_8021Q_INFO); | ||
| 365 | vlan = (struct ndis_pkt_8021q_info *)((void *)ppi + | ||
| 366 | ppi->ppi_offset); | ||
| 367 | vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK; | ||
| 368 | vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >> | ||
| 369 | VLAN_PRIO_SHIFT; | ||
| 370 | } | ||
| 177 | 371 | ||
| 178 | /* Initialize it from the skb */ | 372 | net_trans_info = get_net_transport_info(skb, &hdr_offset); |
| 179 | packet->total_data_buflen = skb->len; | 373 | if (net_trans_info == TRANSPORT_INFO_NOT_IP) |
| 374 | goto do_send; | ||
| 375 | |||
| 376 | /* | ||
| 377 | * Setup the sendside checksum offload only if this is not a | ||
| 378 | * GSO packet. | ||
| 379 | */ | ||
| 380 | if (skb_is_gso(skb)) | ||
| 381 | goto do_lso; | ||
| 382 | |||
| 383 | rndis_msg_size += NDIS_CSUM_PPI_SIZE; | ||
| 384 | ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE, | ||
| 385 | TCPIP_CHKSUM_PKTINFO); | ||
| 386 | |||
| 387 | csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi + | ||
| 388 | ppi->ppi_offset); | ||
| 180 | 389 | ||
| 181 | /* Start filling in the page buffers starting after RNDIS buffer. */ | 390 | if (net_trans_info & (INFO_IPV4 << 16)) |
| 182 | packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; | 391 | csum_info->transmit.is_ipv4 = 1; |
| 183 | packet->page_buf[1].offset | ||
| 184 | = (unsigned long)skb->data & (PAGE_SIZE - 1); | ||
| 185 | if (npg_data == 1) | ||
| 186 | packet->page_buf[1].len = skb_headlen(skb); | ||
| 187 | else | 392 | else |
| 188 | packet->page_buf[1].len = PAGE_SIZE | 393 | csum_info->transmit.is_ipv6 = 1; |
| 189 | - packet->page_buf[1].offset; | 394 | |
| 190 | 395 | if (net_trans_info & INFO_TCP) { | |
| 191 | for (i = 2; i <= npg_data; i++) { | 396 | csum_info->transmit.tcp_checksum = 1; |
| 192 | packet->page_buf[i].pfn = virt_to_phys(skb->data | 397 | csum_info->transmit.tcp_header_offset = hdr_offset; |
| 193 | + PAGE_SIZE * (i-1)) >> PAGE_SHIFT; | 398 | } else if (net_trans_info & INFO_UDP) { |
| 194 | packet->page_buf[i].offset = 0; | 399 | csum_info->transmit.udp_checksum = 1; |
| 195 | packet->page_buf[i].len = PAGE_SIZE; | ||
| 196 | } | 400 | } |
| 197 | if (npg_data > 1) | 401 | goto do_send; |
| 198 | packet->page_buf[npg_data].len = (((unsigned long)skb->data | 402 | |
| 199 | + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1; | 403 | do_lso: |
| 200 | 404 | rndis_msg_size += NDIS_LSO_PPI_SIZE; | |
| 201 | /* Additional fragments are after SKB data */ | 405 | ppi = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE, |
| 202 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | 406 | TCP_LARGESEND_PKTINFO); |
| 203 | const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; | 407 | |
| 204 | 408 | lso_info = (struct ndis_tcp_lso_info *)((void *)ppi + | |
| 205 | packet->page_buf[i+npg_data+1].pfn = | 409 | ppi->ppi_offset); |
| 206 | page_to_pfn(skb_frag_page(f)); | 410 | |
| 207 | packet->page_buf[i+npg_data+1].offset = f->page_offset; | 411 | lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE; |
| 208 | packet->page_buf[i+npg_data+1].len = skb_frag_size(f); | 412 | if (net_trans_info & (INFO_IPV4 << 16)) { |
| 413 | lso_info->lso_v2_transmit.ip_version = | ||
| 414 | NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4; | ||
| 415 | ip_hdr(skb)->tot_len = 0; | ||
| 416 | ip_hdr(skb)->check = 0; | ||
| 417 | tcp_hdr(skb)->check = | ||
| 418 | ~csum_tcpudp_magic(ip_hdr(skb)->saddr, | ||
| 419 | ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); | ||
| 420 | } else { | ||
| 421 | lso_info->lso_v2_transmit.ip_version = | ||
| 422 | NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6; | ||
| 423 | ipv6_hdr(skb)->payload_len = 0; | ||
| 424 | tcp_hdr(skb)->check = | ||
| 425 | ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, | ||
| 426 | &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); | ||
| 209 | } | 427 | } |
| 428 | lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset; | ||
| 429 | lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size; | ||
| 210 | 430 | ||
| 211 | /* Set the completion routine */ | 431 | do_send: |
| 212 | packet->completion.send.send_completion = netvsc_xmit_completion; | 432 | /* Start filling in the page buffers with the rndis hdr */ |
| 213 | packet->completion.send.send_completion_ctx = packet; | 433 | rndis_msg->msg_len += rndis_msg_size; |
| 214 | packet->completion.send.send_completion_tid = (unsigned long)skb; | 434 | packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, |
| 435 | skb, &packet->page_buf[0]); | ||
| 436 | |||
| 437 | ret = netvsc_send(net_device_ctx->device_ctx, packet); | ||
| 215 | 438 | ||
| 216 | ret = rndis_filter_send(net_device_ctx->device_ctx, | ||
| 217 | packet); | ||
| 218 | if (ret == 0) { | 439 | if (ret == 0) { |
| 219 | net->stats.tx_bytes += skb->len; | 440 | net->stats.tx_bytes += skb->len; |
| 220 | net->stats.tx_packets++; | 441 | net->stats.tx_packets++; |
| @@ -264,7 +485,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, | |||
| 264 | * "wire" on the specified device. | 485 | * "wire" on the specified device. |
| 265 | */ | 486 | */ |
| 266 | int netvsc_recv_callback(struct hv_device *device_obj, | 487 | int netvsc_recv_callback(struct hv_device *device_obj, |
| 267 | struct hv_netvsc_packet *packet) | 488 | struct hv_netvsc_packet *packet, |
| 489 | struct ndis_tcp_ip_checksum_info *csum_info) | ||
| 268 | { | 490 | { |
| 269 | struct net_device *net; | 491 | struct net_device *net; |
| 270 | struct sk_buff *skb; | 492 | struct sk_buff *skb; |
| @@ -291,7 +513,17 @@ int netvsc_recv_callback(struct hv_device *device_obj, | |||
| 291 | packet->total_data_buflen); | 513 | packet->total_data_buflen); |
| 292 | 514 | ||
| 293 | skb->protocol = eth_type_trans(skb, net); | 515 | skb->protocol = eth_type_trans(skb, net); |
| 294 | skb->ip_summed = CHECKSUM_NONE; | 516 | if (csum_info) { |
| 517 | /* We only look at the IP checksum here. | ||
| 518 | * Should we be dropping the packet if checksum | ||
| 519 | * failed? How do we deal with other checksums - TCP/UDP? | ||
| 520 | */ | ||
| 521 | if (csum_info->receive.ip_checksum_succeeded) | ||
| 522 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
| 523 | else | ||
| 524 | skb->ip_summed = CHECKSUM_NONE; | ||
| 525 | } | ||
| 526 | |||
| 295 | if (packet->vlan_tci & VLAN_TAG_PRESENT) | 527 | if (packet->vlan_tci & VLAN_TAG_PRESENT) |
| 296 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), | 528 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), |
| 297 | packet->vlan_tci); | 529 | packet->vlan_tci); |
| @@ -450,9 +682,10 @@ static int netvsc_probe(struct hv_device *dev, | |||
| 450 | 682 | ||
| 451 | net->netdev_ops = &device_ops; | 683 | net->netdev_ops = &device_ops; |
| 452 | 684 | ||
| 453 | /* TODO: Add GSO and Checksum offload */ | 685 | net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM | |
| 454 | net->hw_features = 0; | 686 | NETIF_F_TSO; |
| 455 | net->features = NETIF_F_HW_VLAN_CTAG_TX; | 687 | net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM | |
| 688 | NETIF_F_IP_CSUM | NETIF_F_TSO; | ||
| 456 | 689 | ||
| 457 | SET_ETHTOOL_OPS(net, ðtool_ops); | 690 | SET_ETHTOOL_OPS(net, ðtool_ops); |
| 458 | SET_NETDEV_DEV(net, &dev->device); | 691 | SET_NETDEV_DEV(net, &dev->device); |
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index f0cc8ef21e1c..54553df74cd2 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
| @@ -350,6 +350,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev, | |||
| 350 | struct rndis_packet *rndis_pkt; | 350 | struct rndis_packet *rndis_pkt; |
| 351 | u32 data_offset; | 351 | u32 data_offset; |
| 352 | struct ndis_pkt_8021q_info *vlan; | 352 | struct ndis_pkt_8021q_info *vlan; |
| 353 | struct ndis_tcp_ip_checksum_info *csum_info; | ||
| 353 | 354 | ||
| 354 | rndis_pkt = &msg->msg.pkt; | 355 | rndis_pkt = &msg->msg.pkt; |
| 355 | 356 | ||
| @@ -388,7 +389,8 @@ static void rndis_filter_receive_data(struct rndis_device *dev, | |||
| 388 | pkt->vlan_tci = 0; | 389 | pkt->vlan_tci = 0; |
| 389 | } | 390 | } |
| 390 | 391 | ||
| 391 | netvsc_recv_callback(dev->net_dev->dev, pkt); | 392 | csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); |
| 393 | netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info); | ||
| 392 | } | 394 | } |
| 393 | 395 | ||
| 394 | int rndis_filter_receive(struct hv_device *dev, | 396 | int rndis_filter_receive(struct hv_device *dev, |
| @@ -607,6 +609,61 @@ cleanup: | |||
| 607 | return ret; | 609 | return ret; |
| 608 | } | 610 | } |
| 609 | 611 | ||
| 612 | int rndis_filter_set_offload_params(struct hv_device *hdev, | ||
| 613 | struct ndis_offload_params *req_offloads) | ||
| 614 | { | ||
| 615 | struct netvsc_device *nvdev = hv_get_drvdata(hdev); | ||
| 616 | struct rndis_device *rdev = nvdev->extension; | ||
| 617 | struct net_device *ndev = nvdev->ndev; | ||
| 618 | struct rndis_request *request; | ||
| 619 | struct rndis_set_request *set; | ||
| 620 | struct ndis_offload_params *offload_params; | ||
| 621 | struct rndis_set_complete *set_complete; | ||
| 622 | u32 extlen = sizeof(struct ndis_offload_params); | ||
| 623 | int ret, t; | ||
| 624 | |||
| 625 | request = get_rndis_request(rdev, RNDIS_MSG_SET, | ||
| 626 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); | ||
| 627 | if (!request) | ||
| 628 | return -ENOMEM; | ||
| 629 | |||
| 630 | set = &request->request_msg.msg.set_req; | ||
| 631 | set->oid = OID_TCP_OFFLOAD_PARAMETERS; | ||
| 632 | set->info_buflen = extlen; | ||
| 633 | set->info_buf_offset = sizeof(struct rndis_set_request); | ||
| 634 | set->dev_vc_handle = 0; | ||
| 635 | |||
| 636 | offload_params = (struct ndis_offload_params *)((ulong)set + | ||
| 637 | set->info_buf_offset); | ||
| 638 | *offload_params = *req_offloads; | ||
| 639 | offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT; | ||
| 640 | offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3; | ||
| 641 | offload_params->header.size = extlen; | ||
| 642 | |||
| 643 | ret = rndis_filter_send_request(rdev, request); | ||
| 644 | if (ret != 0) | ||
| 645 | goto cleanup; | ||
| 646 | |||
| 647 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 648 | if (t == 0) { | ||
| 649 | netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n"); | ||
| 650 | /* can't put_rndis_request, since we may still receive a | ||
| 651 | * send-completion. | ||
| 652 | */ | ||
| 653 | return -EBUSY; | ||
| 654 | } else { | ||
| 655 | set_complete = &request->response_msg.msg.set_complete; | ||
| 656 | if (set_complete->status != RNDIS_STATUS_SUCCESS) { | ||
| 657 | netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", | ||
| 658 | set_complete->status); | ||
| 659 | ret = -EINVAL; | ||
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 663 | cleanup: | ||
| 664 | put_rndis_request(rdev, request); | ||
| 665 | return ret; | ||
| 666 | } | ||
| 610 | 667 | ||
| 611 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) | 668 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) |
| 612 | { | 669 | { |
| @@ -807,6 +864,7 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 807 | struct netvsc_device *net_device; | 864 | struct netvsc_device *net_device; |
| 808 | struct rndis_device *rndis_device; | 865 | struct rndis_device *rndis_device; |
| 809 | struct netvsc_device_info *device_info = additional_info; | 866 | struct netvsc_device_info *device_info = additional_info; |
| 867 | struct ndis_offload_params offloads; | ||
| 810 | 868 | ||
| 811 | rndis_device = get_rndis_device(); | 869 | rndis_device = get_rndis_device(); |
| 812 | if (!rndis_device) | 870 | if (!rndis_device) |
| @@ -846,6 +904,26 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 846 | 904 | ||
| 847 | memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); | 905 | memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); |
| 848 | 906 | ||
| 907 | /* Turn on the offloads; the host supports all of the relevant | ||
| 908 | * offloads. | ||
| 909 | */ | ||
| 910 | memset(&offloads, 0, sizeof(struct ndis_offload_params)); | ||
| 911 | /* A value of zero means "no change"; now turn on what we | ||
| 912 | * want. | ||
| 913 | */ | ||
| 914 | offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
| 915 | offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
| 916 | offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
| 917 | offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
| 918 | offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
| 919 | offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; | ||
| 920 | |||
| 921 | |||
| 922 | ret = rndis_filter_set_offload_params(dev, &offloads); | ||
| 923 | if (ret) | ||
| 924 | goto err_dev_remv; | ||
| 925 | |||
| 926 | |||
| 849 | rndis_filter_query_device_link_status(rndis_device); | 927 | rndis_filter_query_device_link_status(rndis_device); |
| 850 | 928 | ||
| 851 | device_info->link_state = rndis_device->link_state; | 929 | device_info->link_state = rndis_device->link_state; |
| @@ -855,6 +933,10 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 855 | device_info->link_state ? "down" : "up"); | 933 | device_info->link_state ? "down" : "up"); |
| 856 | 934 | ||
| 857 | return ret; | 935 | return ret; |
| 936 | |||
| 937 | err_dev_remv: | ||
| 938 | rndis_filter_device_remove(dev); | ||
| 939 | return ret; | ||
| 858 | } | 940 | } |
| 859 | 941 | ||
| 860 | void rndis_filter_device_remove(struct hv_device *dev) | 942 | void rndis_filter_device_remove(struct hv_device *dev) |
| @@ -891,69 +973,3 @@ int rndis_filter_close(struct hv_device *dev) | |||
| 891 | 973 | ||
| 892 | return rndis_filter_close_device(nvdev->extension); | 974 | return rndis_filter_close_device(nvdev->extension); |
| 893 | } | 975 | } |
| 894 | |||
| 895 | int rndis_filter_send(struct hv_device *dev, | ||
| 896 | struct hv_netvsc_packet *pkt) | ||
| 897 | { | ||
| 898 | struct rndis_message *rndis_msg; | ||
| 899 | struct rndis_packet *rndis_pkt; | ||
| 900 | u32 rndis_msg_size; | ||
| 901 | bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT; | ||
| 902 | |||
| 903 | /* Add the rndis header */ | ||
| 904 | rndis_msg = (struct rndis_message *)pkt->extension; | ||
| 905 | |||
| 906 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); | ||
| 907 | if (isvlan) | ||
| 908 | rndis_msg_size += NDIS_VLAN_PPI_SIZE; | ||
| 909 | |||
| 910 | rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; | ||
| 911 | rndis_msg->msg_len = pkt->total_data_buflen + | ||
| 912 | rndis_msg_size; | ||
| 913 | |||
| 914 | rndis_pkt = &rndis_msg->msg.pkt; | ||
| 915 | rndis_pkt->data_offset = sizeof(struct rndis_packet); | ||
| 916 | if (isvlan) | ||
| 917 | rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE; | ||
| 918 | rndis_pkt->data_len = pkt->total_data_buflen; | ||
| 919 | |||
| 920 | if (isvlan) { | ||
| 921 | struct rndis_per_packet_info *ppi; | ||
| 922 | struct ndis_pkt_8021q_info *vlan; | ||
| 923 | |||
| 924 | rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); | ||
| 925 | rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE; | ||
| 926 | |||
| 927 | ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt + | ||
| 928 | rndis_pkt->per_pkt_info_offset); | ||
| 929 | ppi->size = NDIS_VLAN_PPI_SIZE; | ||
| 930 | ppi->type = IEEE_8021Q_INFO; | ||
| 931 | ppi->ppi_offset = sizeof(struct rndis_per_packet_info); | ||
| 932 | |||
| 933 | vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi + | ||
| 934 | ppi->ppi_offset); | ||
| 935 | vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK; | ||
| 936 | vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | ||
| 937 | } | ||
| 938 | |||
| 939 | pkt->is_data_pkt = true; | ||
| 940 | pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT; | ||
| 941 | pkt->page_buf[0].offset = | ||
| 942 | (unsigned long)rndis_msg & (PAGE_SIZE-1); | ||
| 943 | pkt->page_buf[0].len = rndis_msg_size; | ||
| 944 | |||
| 945 | /* Add one page_buf if the rndis msg goes beyond page boundary */ | ||
| 946 | if (pkt->page_buf[0].offset + rndis_msg_size > PAGE_SIZE) { | ||
| 947 | int i; | ||
| 948 | for (i = pkt->page_buf_cnt; i > 1; i--) | ||
| 949 | pkt->page_buf[i] = pkt->page_buf[i-1]; | ||
| 950 | pkt->page_buf_cnt++; | ||
| 951 | pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; | ||
| 952 | pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) | ||
| 953 | rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT; | ||
| 954 | pkt->page_buf[1].offset = 0; | ||
| 955 | pkt->page_buf[1].len = rndis_msg_size - pkt->page_buf[0].len; | ||
| 956 | } | ||
| 957 | |||
| 958 | return netvsc_send(dev, pkt); | ||
| 959 | } | ||
