aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/hyperv
diff options
context:
space:
mode:
authorHaiyang Zhang <haiyangz@microsoft.com>2012-03-12 06:20:50 -0400
committerDavid S. Miller <davem@davemloft.net>2012-03-12 20:06:48 -0400
commit1f5f3a75e216fe771b8d6805e0bb2f43595a6ee1 (patch)
treefc1f3e0c22c908dee660a565fe39f2416bca937b /drivers/net/hyperv
parentef31bef6216db76950c38f1993b45953402f4c63 (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.h34
-rw-r--r--drivers/net/hyperv/netvsc.c3
-rw-r--r--drivers/net/hyperv/netvsc_drv.c8
-rw-r--r--drivers/net/hyperv/rndis_filter.c60
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 {
926struct rndis_per_packet_info { 927struct 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
933enum 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
949struct 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. */
934struct rndis_config_parameter_info { 966struct 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, &ethtool_ops); 416 SET_ETHTOOL_OPS(net, &ethtool_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 */
311static 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
306static void rndis_filter_receive_data(struct rndis_device *dev, 333static 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 =