diff options
author | Haiyang Zhang <haiyangz@microsoft.com> | 2012-03-12 06:20:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-03-12 20:06:48 -0400 |
commit | 1f5f3a75e216fe771b8d6805e0bb2f43595a6ee1 (patch) | |
tree | fc1f3e0c22c908dee660a565fe39f2416bca937b /drivers/net/hyperv | |
parent | ef31bef6216db76950c38f1993b45953402f4c63 (diff) |
net/hyperv: Add support for vlan trunking from guests
With this feature, a Linux guest can now configure multiple vlans through
a single synthetic NIC on Win8 Hyper-V host.
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/hyperv')
-rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 34 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 3 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 8 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 60 |
4 files changed, 101 insertions, 4 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index dec5836ae075..c35824552792 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
@@ -49,6 +49,7 @@ struct hv_netvsc_packet { | |||
49 | 49 | ||
50 | struct hv_device *device; | 50 | struct hv_device *device; |
51 | bool is_data_pkt; | 51 | bool is_data_pkt; |
52 | u16 vlan_tci; | ||
52 | 53 | ||
53 | /* | 54 | /* |
54 | * Valid only for receives when we break a xfer page packet | 55 | * Valid only for receives when we break a xfer page packet |
@@ -926,9 +927,40 @@ struct rndis_oobd { | |||
926 | struct rndis_per_packet_info { | 927 | struct rndis_per_packet_info { |
927 | u32 size; | 928 | u32 size; |
928 | u32 type; | 929 | u32 type; |
929 | u32 per_pkt_info_offset; | 930 | u32 ppi_offset; |
931 | }; | ||
932 | |||
933 | enum ndis_per_pkt_info_type { | ||
934 | TCPIP_CHKSUM_PKTINFO, | ||
935 | IPSEC_PKTINFO, | ||
936 | TCP_LARGESEND_PKTINFO, | ||
937 | CLASSIFICATION_HANDLE_PKTINFO, | ||
938 | NDIS_RESERVED, | ||
939 | SG_LIST_PKTINFO, | ||
940 | IEEE_8021Q_INFO, | ||
941 | ORIGINAL_PKTINFO, | ||
942 | PACKET_CANCEL_ID, | ||
943 | ORIGINAL_NET_BUFLIST, | ||
944 | CACHED_NET_BUFLIST, | ||
945 | SHORT_PKT_PADINFO, | ||
946 | MAX_PER_PKT_INFO | ||
947 | }; | ||
948 | |||
949 | struct ndis_pkt_8021q_info { | ||
950 | union { | ||
951 | struct { | ||
952 | u32 pri:3; /* User Priority */ | ||
953 | u32 cfi:1; /* Canonical Format ID */ | ||
954 | u32 vlanid:12; /* VLAN ID */ | ||
955 | u32 reserved:16; | ||
956 | }; | ||
957 | u32 value; | ||
958 | }; | ||
930 | }; | 959 | }; |
931 | 960 | ||
961 | #define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | ||
962 | sizeof(struct ndis_pkt_8021q_info)) | ||
963 | |||
932 | /* Format of Information buffer passed in a SetRequest for the OID */ | 964 | /* Format of Information buffer passed in a SetRequest for the OID */ |
933 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ | 965 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ |
934 | struct rndis_config_parameter_info { | 966 | struct rndis_config_parameter_info { |
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 8965b45ce5a5..d025c83cd12a 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c | |||
@@ -300,6 +300,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, | |||
300 | memset(init_packet, 0, sizeof(struct nvsp_message)); | 300 | memset(init_packet, 0, sizeof(struct nvsp_message)); |
301 | init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; | 301 | init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; |
302 | init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu; | 302 | init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu; |
303 | init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1; | ||
303 | 304 | ||
304 | ret = vmbus_sendpacket(device->channel, init_packet, | 305 | ret = vmbus_sendpacket(device->channel, init_packet, |
305 | sizeof(struct nvsp_message), | 306 | sizeof(struct nvsp_message), |
@@ -341,7 +342,7 @@ static int netvsc_connect_vsp(struct hv_device *device) | |||
341 | /* Send the ndis version */ | 342 | /* Send the ndis version */ |
342 | memset(init_packet, 0, sizeof(struct nvsp_message)); | 343 | memset(init_packet, 0, sizeof(struct nvsp_message)); |
343 | 344 | ||
344 | ndis_version = 0x00050000; | 345 | ndis_version = 0x00050001; |
345 | 346 | ||
346 | init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER; | 347 | init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER; |
347 | init_packet->msg.v1_msg. | 348 | init_packet->msg.v1_msg. |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 217dfedbba20..0f8e8344891b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c | |||
@@ -159,7 +159,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
159 | /* Allocate a netvsc packet based on # of frags. */ | 159 | /* Allocate a netvsc packet based on # of frags. */ |
160 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | 160 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + |
161 | (num_pages * sizeof(struct hv_page_buffer)) + | 161 | (num_pages * sizeof(struct hv_page_buffer)) + |
162 | sizeof(struct rndis_filter_packet), GFP_ATOMIC); | 162 | sizeof(struct rndis_filter_packet) + |
163 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); | ||
163 | if (!packet) { | 164 | if (!packet) { |
164 | /* out of memory, drop packet */ | 165 | /* out of memory, drop packet */ |
165 | netdev_err(net, "unable to allocate hv_netvsc_packet\n"); | 166 | netdev_err(net, "unable to allocate hv_netvsc_packet\n"); |
@@ -169,6 +170,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
169 | return NETDEV_TX_BUSY; | 170 | return NETDEV_TX_BUSY; |
170 | } | 171 | } |
171 | 172 | ||
173 | packet->vlan_tci = skb->vlan_tci; | ||
174 | |||
172 | packet->extension = (void *)(unsigned long)packet + | 175 | packet->extension = (void *)(unsigned long)packet + |
173 | sizeof(struct hv_netvsc_packet) + | 176 | sizeof(struct hv_netvsc_packet) + |
174 | (num_pages * sizeof(struct hv_page_buffer)); | 177 | (num_pages * sizeof(struct hv_page_buffer)); |
@@ -293,6 +296,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, | |||
293 | 296 | ||
294 | skb->protocol = eth_type_trans(skb, net); | 297 | skb->protocol = eth_type_trans(skb, net); |
295 | skb->ip_summed = CHECKSUM_NONE; | 298 | skb->ip_summed = CHECKSUM_NONE; |
299 | skb->vlan_tci = packet->vlan_tci; | ||
296 | 300 | ||
297 | net->stats.rx_packets++; | 301 | net->stats.rx_packets++; |
298 | net->stats.rx_bytes += packet->total_data_buflen; | 302 | net->stats.rx_bytes += packet->total_data_buflen; |
@@ -407,7 +411,7 @@ static int netvsc_probe(struct hv_device *dev, | |||
407 | 411 | ||
408 | /* TODO: Add GSO and Checksum offload */ | 412 | /* TODO: Add GSO and Checksum offload */ |
409 | net->hw_features = NETIF_F_SG; | 413 | net->hw_features = NETIF_F_SG; |
410 | net->features = NETIF_F_SG; | 414 | net->features = NETIF_F_SG | NETIF_F_HW_VLAN_TX; |
411 | 415 | ||
412 | SET_ETHTOOL_OPS(net, ðtool_ops); | 416 | SET_ETHTOOL_OPS(net, ðtool_ops); |
413 | SET_NETDEV_DEV(net, &dev->device); | 417 | SET_NETDEV_DEV(net, &dev->device); |
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 0c3d7d9f51d3..d6be64bcefd4 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/io.h> | 26 | #include <linux/io.h> |
27 | #include <linux/if_ether.h> | 27 | #include <linux/if_ether.h> |
28 | #include <linux/netdevice.h> | 28 | #include <linux/netdevice.h> |
29 | #include <linux/if_vlan.h> | ||
29 | 30 | ||
30 | #include "hyperv_net.h" | 31 | #include "hyperv_net.h" |
31 | 32 | ||
@@ -303,12 +304,39 @@ static void rndis_filter_receive_indicate_status(struct rndis_device *dev, | |||
303 | } | 304 | } |
304 | } | 305 | } |
305 | 306 | ||
307 | /* | ||
308 | * Get the Per-Packet-Info with the specified type | ||
309 | * return NULL if not found. | ||
310 | */ | ||
311 | static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) | ||
312 | { | ||
313 | struct rndis_per_packet_info *ppi; | ||
314 | int len; | ||
315 | |||
316 | if (rpkt->per_pkt_info_offset == 0) | ||
317 | return NULL; | ||
318 | |||
319 | ppi = (struct rndis_per_packet_info *)((ulong)rpkt + | ||
320 | rpkt->per_pkt_info_offset); | ||
321 | len = rpkt->per_pkt_info_len; | ||
322 | |||
323 | while (len > 0) { | ||
324 | if (ppi->type == type) | ||
325 | return (void *)((ulong)ppi + ppi->ppi_offset); | ||
326 | len -= ppi->size; | ||
327 | ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size); | ||
328 | } | ||
329 | |||
330 | return NULL; | ||
331 | } | ||
332 | |||
306 | static void rndis_filter_receive_data(struct rndis_device *dev, | 333 | static void rndis_filter_receive_data(struct rndis_device *dev, |
307 | struct rndis_message *msg, | 334 | struct rndis_message *msg, |
308 | struct hv_netvsc_packet *pkt) | 335 | struct hv_netvsc_packet *pkt) |
309 | { | 336 | { |
310 | struct rndis_packet *rndis_pkt; | 337 | struct rndis_packet *rndis_pkt; |
311 | u32 data_offset; | 338 | u32 data_offset; |
339 | struct ndis_pkt_8021q_info *vlan; | ||
312 | 340 | ||
313 | rndis_pkt = &msg->msg.pkt; | 341 | rndis_pkt = &msg->msg.pkt; |
314 | 342 | ||
@@ -344,6 +372,14 @@ static void rndis_filter_receive_data(struct rndis_device *dev, | |||
344 | 372 | ||
345 | pkt->is_data_pkt = true; | 373 | pkt->is_data_pkt = true; |
346 | 374 | ||
375 | vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); | ||
376 | if (vlan) { | ||
377 | pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | | ||
378 | (vlan->pri << VLAN_PRIO_SHIFT); | ||
379 | } else { | ||
380 | pkt->vlan_tci = 0; | ||
381 | } | ||
382 | |||
347 | netvsc_recv_callback(dev->net_dev->dev, pkt); | 383 | netvsc_recv_callback(dev->net_dev->dev, pkt); |
348 | } | 384 | } |
349 | 385 | ||
@@ -759,12 +795,15 @@ int rndis_filter_send(struct hv_device *dev, | |||
759 | struct rndis_message *rndis_msg; | 795 | struct rndis_message *rndis_msg; |
760 | struct rndis_packet *rndis_pkt; | 796 | struct rndis_packet *rndis_pkt; |
761 | u32 rndis_msg_size; | 797 | u32 rndis_msg_size; |
798 | bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT; | ||
762 | 799 | ||
763 | /* Add the rndis header */ | 800 | /* Add the rndis header */ |
764 | filter_pkt = (struct rndis_filter_packet *)pkt->extension; | 801 | filter_pkt = (struct rndis_filter_packet *)pkt->extension; |
765 | 802 | ||
766 | rndis_msg = &filter_pkt->msg; | 803 | rndis_msg = &filter_pkt->msg; |
767 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); | 804 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); |
805 | if (isvlan) | ||
806 | rndis_msg_size += NDIS_VLAN_PPI_SIZE; | ||
768 | 807 | ||
769 | rndis_msg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; | 808 | rndis_msg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; |
770 | rndis_msg->msg_len = pkt->total_data_buflen + | 809 | rndis_msg->msg_len = pkt->total_data_buflen + |
@@ -772,8 +811,29 @@ int rndis_filter_send(struct hv_device *dev, | |||
772 | 811 | ||
773 | rndis_pkt = &rndis_msg->msg.pkt; | 812 | rndis_pkt = &rndis_msg->msg.pkt; |
774 | rndis_pkt->data_offset = sizeof(struct rndis_packet); | 813 | rndis_pkt->data_offset = sizeof(struct rndis_packet); |
814 | if (isvlan) | ||
815 | rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE; | ||
775 | rndis_pkt->data_len = pkt->total_data_buflen; | 816 | rndis_pkt->data_len = pkt->total_data_buflen; |
776 | 817 | ||
818 | if (isvlan) { | ||
819 | struct rndis_per_packet_info *ppi; | ||
820 | struct ndis_pkt_8021q_info *vlan; | ||
821 | |||
822 | rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); | ||
823 | rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE; | ||
824 | |||
825 | ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt + | ||
826 | rndis_pkt->per_pkt_info_offset); | ||
827 | ppi->size = NDIS_VLAN_PPI_SIZE; | ||
828 | ppi->type = IEEE_8021Q_INFO; | ||
829 | ppi->ppi_offset = sizeof(struct rndis_per_packet_info); | ||
830 | |||
831 | vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi + | ||
832 | ppi->ppi_offset); | ||
833 | vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK; | ||
834 | vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | ||
835 | } | ||
836 | |||
777 | pkt->is_data_pkt = true; | 837 | pkt->is_data_pkt = true; |
778 | pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT; | 838 | pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT; |
779 | pkt->page_buf[0].offset = | 839 | pkt->page_buf[0].offset = |