1
From: Haiyang Zhang <haiyangz@microsoft.com>
2
Date: Mon, 12 Mar 2012 10:20:50 +0000
3
Subject: [PATCH 70/77] net/hyperv: Add support for vlan trunking from guests
5
commit 1f5f3a75e216fe771b8d6805e0bb2f43595a6ee1 upstream.
7
With this feature, a Linux guest can now configure multiple vlans through
8
a single synthetic NIC on Win8 Hyper-V host.
10
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
11
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
12
Signed-off-by: David S. Miller <davem@davemloft.net>
14
drivers/net/hyperv/hyperv_net.h | 34 ++++++++++++++++++++-
15
drivers/net/hyperv/netvsc.c | 3 +-
16
drivers/net/hyperv/netvsc_drv.c | 8 +++--
17
drivers/net/hyperv/rndis_filter.c | 60 +++++++++++++++++++++++++++++++++++++
18
4 files changed, 101 insertions(+), 4 deletions(-)
20
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
21
index dec5836..c358245 100644
22
--- a/drivers/net/hyperv/hyperv_net.h
23
+++ b/drivers/net/hyperv/hyperv_net.h
24
@@ -49,6 +49,7 @@ struct hv_netvsc_packet {
26
struct hv_device *device;
31
* Valid only for receives when we break a xfer page packet
32
@@ -926,9 +927,40 @@ struct rndis_oobd {
33
struct rndis_per_packet_info {
36
- u32 per_pkt_info_offset;
40
+enum ndis_per_pkt_info_type {
41
+ TCPIP_CHKSUM_PKTINFO,
43
+ TCP_LARGESEND_PKTINFO,
44
+ CLASSIFICATION_HANDLE_PKTINFO,
50
+ ORIGINAL_NET_BUFLIST,
56
+struct ndis_pkt_8021q_info {
59
+ u32 pri:3; /* User Priority */
60
+ u32 cfi:1; /* Canonical Format ID */
61
+ u32 vlanid:12; /* VLAN ID */
68
+#define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
69
+ sizeof(struct ndis_pkt_8021q_info))
71
/* Format of Information buffer passed in a SetRequest for the OID */
72
/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
73
struct rndis_config_parameter_info {
74
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
75
index 8965b45..d025c83 100644
76
--- a/drivers/net/hyperv/netvsc.c
77
+++ b/drivers/net/hyperv/netvsc.c
78
@@ -300,6 +300,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
79
memset(init_packet, 0, sizeof(struct nvsp_message));
80
init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
81
init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu;
82
+ init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1;
84
ret = vmbus_sendpacket(device->channel, init_packet,
85
sizeof(struct nvsp_message),
86
@@ -341,7 +342,7 @@ static int netvsc_connect_vsp(struct hv_device *device)
87
/* Send the ndis version */
88
memset(init_packet, 0, sizeof(struct nvsp_message));
90
- ndis_version = 0x00050000;
91
+ ndis_version = 0x00050001;
93
init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER;
94
init_packet->msg.v1_msg.
95
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
96
index 217dfed..0f8e834 100644
97
--- a/drivers/net/hyperv/netvsc_drv.c
98
+++ b/drivers/net/hyperv/netvsc_drv.c
99
@@ -159,7 +159,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
100
/* Allocate a netvsc packet based on # of frags. */
101
packet = kzalloc(sizeof(struct hv_netvsc_packet) +
102
(num_pages * sizeof(struct hv_page_buffer)) +
103
- sizeof(struct rndis_filter_packet), GFP_ATOMIC);
104
+ sizeof(struct rndis_filter_packet) +
105
+ NDIS_VLAN_PPI_SIZE, GFP_ATOMIC);
107
/* out of memory, drop packet */
108
netdev_err(net, "unable to allocate hv_netvsc_packet\n");
109
@@ -169,6 +170,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
110
return NETDEV_TX_BUSY;
113
+ packet->vlan_tci = skb->vlan_tci;
115
packet->extension = (void *)(unsigned long)packet +
116
sizeof(struct hv_netvsc_packet) +
117
(num_pages * sizeof(struct hv_page_buffer));
118
@@ -293,6 +296,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
120
skb->protocol = eth_type_trans(skb, net);
121
skb->ip_summed = CHECKSUM_NONE;
122
+ skb->vlan_tci = packet->vlan_tci;
124
net->stats.rx_packets++;
125
net->stats.rx_bytes += packet->total_data_buflen;
126
@@ -407,7 +411,7 @@ static int netvsc_probe(struct hv_device *dev,
128
/* TODO: Add GSO and Checksum offload */
129
net->hw_features = NETIF_F_SG;
130
- net->features = NETIF_F_SG;
131
+ net->features = NETIF_F_SG | NETIF_F_HW_VLAN_TX;
133
SET_ETHTOOL_OPS(net, ðtool_ops);
134
SET_NETDEV_DEV(net, &dev->device);
135
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
136
index 0c3d7d9..d6be64b 100644
137
--- a/drivers/net/hyperv/rndis_filter.c
138
+++ b/drivers/net/hyperv/rndis_filter.c
140
#include <linux/io.h>
141
#include <linux/if_ether.h>
142
#include <linux/netdevice.h>
143
+#include <linux/if_vlan.h>
145
#include "hyperv_net.h"
147
@@ -303,12 +304,39 @@ static void rndis_filter_receive_indicate_status(struct rndis_device *dev,
152
+ * Get the Per-Packet-Info with the specified type
153
+ * return NULL if not found.
155
+static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
157
+ struct rndis_per_packet_info *ppi;
160
+ if (rpkt->per_pkt_info_offset == 0)
163
+ ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
164
+ rpkt->per_pkt_info_offset);
165
+ len = rpkt->per_pkt_info_len;
168
+ if (ppi->type == type)
169
+ return (void *)((ulong)ppi + ppi->ppi_offset);
171
+ ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
177
static void rndis_filter_receive_data(struct rndis_device *dev,
178
struct rndis_message *msg,
179
struct hv_netvsc_packet *pkt)
181
struct rndis_packet *rndis_pkt;
183
+ struct ndis_pkt_8021q_info *vlan;
185
rndis_pkt = &msg->msg.pkt;
187
@@ -344,6 +372,14 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
189
pkt->is_data_pkt = true;
191
+ vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
193
+ pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
194
+ (vlan->pri << VLAN_PRIO_SHIFT);
199
netvsc_recv_callback(dev->net_dev->dev, pkt);
202
@@ -759,12 +795,15 @@ int rndis_filter_send(struct hv_device *dev,
203
struct rndis_message *rndis_msg;
204
struct rndis_packet *rndis_pkt;
206
+ bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT;
208
/* Add the rndis header */
209
filter_pkt = (struct rndis_filter_packet *)pkt->extension;
211
rndis_msg = &filter_pkt->msg;
212
rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
214
+ rndis_msg_size += NDIS_VLAN_PPI_SIZE;
216
rndis_msg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
217
rndis_msg->msg_len = pkt->total_data_buflen +
218
@@ -772,8 +811,29 @@ int rndis_filter_send(struct hv_device *dev,
220
rndis_pkt = &rndis_msg->msg.pkt;
221
rndis_pkt->data_offset = sizeof(struct rndis_packet);
223
+ rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE;
224
rndis_pkt->data_len = pkt->total_data_buflen;
227
+ struct rndis_per_packet_info *ppi;
228
+ struct ndis_pkt_8021q_info *vlan;
230
+ rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet);
231
+ rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE;
233
+ ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt +
234
+ rndis_pkt->per_pkt_info_offset);
235
+ ppi->size = NDIS_VLAN_PPI_SIZE;
236
+ ppi->type = IEEE_8021Q_INFO;
237
+ ppi->ppi_offset = sizeof(struct rndis_per_packet_info);
239
+ vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi +
241
+ vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK;
242
+ vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
245
pkt->is_data_pkt = true;
246
pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT;
247
pkt->page_buf[0].offset =