summaryrefslogtreecommitdiffstats
path: root/drivers/net/hyperv
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-04-14 13:18:27 +0200
committerIngo Molnar <mingo@kernel.org>2012-04-14 13:19:04 +0200
commit6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch)
tree021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /drivers/net/hyperv
parent682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff)
parenta385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff)
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree), to prepare for tooling changes, and also to pick up v3.4 MM changes that the uprobes code needs to take care of. Signed-off-by: Ingo Molnar <mingo@kernel.org>
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.c24
-rw-r--r--drivers/net/hyperv/rndis_filter.c159
4 files changed, 147 insertions, 73 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index dec5836ae07..c3582455279 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -49,6 +49,7 @@ struct hv_netvsc_packet {
struct hv_device *device;
bool is_data_pkt;
+ u16 vlan_tci;
/*
* Valid only for receives when we break a xfer page packet
@@ -926,9 +927,40 @@ struct rndis_oobd {
struct rndis_per_packet_info {
u32 size;
u32 type;
- u32 per_pkt_info_offset;
+ u32 ppi_offset;
+};
+
+enum ndis_per_pkt_info_type {
+ TCPIP_CHKSUM_PKTINFO,
+ IPSEC_PKTINFO,
+ TCP_LARGESEND_PKTINFO,
+ CLASSIFICATION_HANDLE_PKTINFO,
+ NDIS_RESERVED,
+ SG_LIST_PKTINFO,
+ IEEE_8021Q_INFO,
+ ORIGINAL_PKTINFO,
+ PACKET_CANCEL_ID,
+ ORIGINAL_NET_BUFLIST,
+ CACHED_NET_BUFLIST,
+ SHORT_PKT_PADINFO,
+ MAX_PER_PKT_INFO
+};
+
+struct ndis_pkt_8021q_info {
+ union {
+ struct {
+ u32 pri:3; /* User Priority */
+ u32 cfi:1; /* Canonical Format ID */
+ u32 vlanid:12; /* VLAN ID */
+ u32 reserved:16;
+ };
+ u32 value;
+ };
};
+#define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
+ sizeof(struct ndis_pkt_8021q_info))
+
/* Format of Information buffer passed in a SetRequest for the OID */
/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
struct rndis_config_parameter_info {
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 8965b45ce5a..d025c83cd12 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,
memset(init_packet, 0, sizeof(struct nvsp_message));
init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu;
+ init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1;
ret = vmbus_sendpacket(device->channel, init_packet,
sizeof(struct nvsp_message),
@@ -341,7 +342,7 @@ static int netvsc_connect_vsp(struct hv_device *device)
/* Send the ndis version */
memset(init_packet, 0, sizeof(struct nvsp_message));
- ndis_version = 0x00050000;
+ ndis_version = 0x00050001;
init_packet->hdr.msg_type = NVSP_MSG1_TYPE_SEND_NDIS_VER;
init_packet->msg.v1_msg.
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 466c58a7353..dd294783b5c 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -159,16 +159,19 @@ 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_pages * sizeof(struct hv_page_buffer)) +
- sizeof(struct rndis_filter_packet), GFP_ATOMIC);
+ sizeof(struct rndis_filter_packet) +
+ NDIS_VLAN_PPI_SIZE, GFP_ATOMIC);
if (!packet) {
/* out of memory, drop packet */
netdev_err(net, "unable to allocate hv_netvsc_packet\n");
dev_kfree_skb(skb);
net->stats.tx_dropped++;
- return NETDEV_TX_BUSY;
+ return NETDEV_TX_OK;
}
+ packet->vlan_tci = skb->vlan_tci;
+
packet->extension = (void *)(unsigned long)packet +
sizeof(struct hv_netvsc_packet) +
(num_pages * sizeof(struct hv_page_buffer));
@@ -220,10 +223,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
net->stats.tx_bytes += skb->len;
net->stats.tx_packets++;
} else {
- /* we are shutting down or bus overloaded, just drop packet */
- net->stats.tx_dropped++;
kfree(packet);
- dev_kfree_skb_any(skb);
}
return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK;
@@ -267,13 +267,10 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet)
{
- struct net_device *net = dev_get_drvdata(&device_obj->device);
+ struct net_device *net;
struct sk_buff *skb;
- struct netvsc_device *net_device;
-
- net_device = hv_get_drvdata(device_obj);
- net = net_device->ndev;
+ net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev;
if (!net) {
netdev_err(net, "got receive callback but net device"
" not initialized yet\n");
@@ -296,6 +293,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
skb->protocol = eth_type_trans(skb, net);
skb->ip_summed = CHECKSUM_NONE;
+ skb->vlan_tci = packet->vlan_tci;
net->stats.rx_packets++;
net->stats.rx_bytes += packet->total_data_buflen;
@@ -313,7 +311,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
static void netvsc_get_drvinfo(struct net_device *net,
struct ethtool_drvinfo *info)
{
- strcpy(info->driver, "hv_netvsc");
+ strcpy(info->driver, KBUILD_MODNAME);
strcpy(info->version, HV_DRV_VERSION);
strcpy(info->fw_version, "N/A");
}
@@ -410,7 +408,7 @@ static int netvsc_probe(struct hv_device *dev,
/* TODO: Add GSO and Checksum offload */
net->hw_features = NETIF_F_SG;
- net->features = NETIF_F_SG;
+ net->features = NETIF_F_SG | NETIF_F_HW_VLAN_TX;
SET_ETHTOOL_OPS(net, &ethtool_ops);
SET_NETDEV_DEV(net, &dev->device);
@@ -485,7 +483,7 @@ MODULE_DEVICE_TABLE(vmbus, id_table);
/* The one and only one */
static struct hv_driver netvsc_drv = {
- .name = "netvsc",
+ .name = KBUILD_MODNAME,
.id_table = id_table,
.probe = netvsc_probe,
.remove = netvsc_remove,
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index 133b7fbf859..d6be64bcefd 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -26,6 +26,7 @@
#include <linux/io.h>
#include <linux/if_ether.h>
#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
#include "hyperv_net.h"
@@ -303,12 +304,39 @@ static void rndis_filter_receive_indicate_status(struct rndis_device *dev,
}
}
+/*
+ * Get the Per-Packet-Info with the specified type
+ * return NULL if not found.
+ */
+static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
+{
+ struct rndis_per_packet_info *ppi;
+ int len;
+
+ if (rpkt->per_pkt_info_offset == 0)
+ return NULL;
+
+ ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
+ rpkt->per_pkt_info_offset);
+ len = rpkt->per_pkt_info_len;
+
+ while (len > 0) {
+ if (ppi->type == type)
+ return (void *)((ulong)ppi + ppi->ppi_offset);
+ len -= ppi->size;
+ ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
+ }
+
+ return NULL;
+}
+
static void rndis_filter_receive_data(struct rndis_device *dev,
struct rndis_message *msg,
struct hv_netvsc_packet *pkt)
{
struct rndis_packet *rndis_pkt;
u32 data_offset;
+ struct ndis_pkt_8021q_info *vlan;
rndis_pkt = &msg->msg.pkt;
@@ -344,6 +372,14 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
pkt->is_data_pkt = true;
+ vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
+ if (vlan) {
+ pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
+ (vlan->pri << VLAN_PRIO_SHIFT);
+ } else {
+ pkt->vlan_tci = 0;
+ }
+
netvsc_recv_callback(dev->net_dev->dev, pkt);
}
@@ -352,8 +388,7 @@ int rndis_filter_receive(struct hv_device *dev,
{
struct netvsc_device *net_dev = hv_get_drvdata(dev);
struct rndis_device *rndis_dev;
- struct rndis_message rndis_msg;
- struct rndis_message *rndis_hdr;
+ struct rndis_message *rndis_msg;
struct net_device *ndev;
if (!net_dev)
@@ -375,46 +410,32 @@ int rndis_filter_receive(struct hv_device *dev,
return -ENODEV;
}
- rndis_hdr = pkt->data;
-
- /* Make sure we got a valid rndis message */
- if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) &&
- (rndis_hdr->msg_len > sizeof(struct rndis_message))) {
- netdev_err(ndev, "incoming rndis message buffer overflow "
- "detected (got %u, max %zu)..marking it an error!\n",
- rndis_hdr->msg_len,
- sizeof(struct rndis_message));
- }
+ rndis_msg = pkt->data;
- memcpy(&rndis_msg, rndis_hdr,
- (rndis_hdr->msg_len > sizeof(struct rndis_message)) ?
- sizeof(struct rndis_message) :
- rndis_hdr->msg_len);
+ dump_rndis_message(dev, rndis_msg);
- dump_rndis_message(dev, &rndis_msg);
-
- switch (rndis_msg.ndis_msg_type) {
+ switch (rndis_msg->ndis_msg_type) {
case REMOTE_NDIS_PACKET_MSG:
/* data msg */
- rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt);
+ rndis_filter_receive_data(rndis_dev, rndis_msg, pkt);
break;
case REMOTE_NDIS_INITIALIZE_CMPLT:
case REMOTE_NDIS_QUERY_CMPLT:
case REMOTE_NDIS_SET_CMPLT:
/* completion msgs */
- rndis_filter_receive_response(rndis_dev, &rndis_msg);
+ rndis_filter_receive_response(rndis_dev, rndis_msg);
break;
case REMOTE_NDIS_INDICATE_STATUS_MSG:
/* notification msgs */
- rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg);
+ rndis_filter_receive_indicate_status(rndis_dev, rndis_msg);
break;
default:
netdev_err(ndev,
"unhandled rndis message (type %u len %u)\n",
- rndis_msg.ndis_msg_type,
- rndis_msg.msg_len);
+ rndis_msg->ndis_msg_type,
+ rndis_msg->msg_len);
break;
}
@@ -758,66 +779,88 @@ int rndis_filter_open(struct hv_device *dev)
int rndis_filter_close(struct hv_device *dev)
{
- struct netvsc_device *netDevice = hv_get_drvdata(dev);
+ struct netvsc_device *nvdev = hv_get_drvdata(dev);
- if (!netDevice)
+ if (!nvdev)
return -EINVAL;
- return rndis_filter_close_device(netDevice->extension);
+ return rndis_filter_close_device(nvdev->extension);
}
int rndis_filter_send(struct hv_device *dev,
struct hv_netvsc_packet *pkt)
{
int ret;
- struct rndis_filter_packet *filterPacket;
- struct rndis_message *rndisMessage;
- struct rndis_packet *rndisPacket;
- u32 rndisMessageSize;
+ struct rndis_filter_packet *filter_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 */
- filterPacket = (struct rndis_filter_packet *)pkt->extension;
-
- memset(filterPacket, 0, sizeof(struct rndis_filter_packet));
-
- rndisMessage = &filterPacket->msg;
- rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet);
-
- rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
- rndisMessage->msg_len = pkt->total_data_buflen +
- rndisMessageSize;
-
- rndisPacket = &rndisMessage->msg.pkt;
- rndisPacket->data_offset = sizeof(struct rndis_packet);
- rndisPacket->data_len = pkt->total_data_buflen;
+ filter_pkt = (struct rndis_filter_packet *)pkt->extension;
+
+ rndis_msg = &filter_pkt->msg;
+ rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
+ if (isvlan)
+ rndis_msg_size += NDIS_VLAN_PPI_SIZE;
+
+ rndis_msg->ndis_msg_type = REMOTE_NDIS_PACKET_MSG;
+ 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(rndisMessage) >> PAGE_SHIFT;
+ pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT;
pkt->page_buf[0].offset =
- (unsigned long)rndisMessage & (PAGE_SIZE-1);
- pkt->page_buf[0].len = rndisMessageSize;
+ (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 + rndisMessageSize > PAGE_SIZE) {
+ 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)
- rndisMessage + pkt->page_buf[0].len)) >> PAGE_SHIFT;
+ rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT;
pkt->page_buf[1].offset = 0;
- pkt->page_buf[1].len = rndisMessageSize - pkt->page_buf[0].len;
+ pkt->page_buf[1].len = rndis_msg_size - pkt->page_buf[0].len;
}
/* Save the packet send completion and context */
- filterPacket->completion = pkt->completion.send.send_completion;
- filterPacket->completion_ctx =
+ filter_pkt->completion = pkt->completion.send.send_completion;
+ filter_pkt->completion_ctx =
pkt->completion.send.send_completion_ctx;
/* Use ours */
pkt->completion.send.send_completion = rndis_filter_send_completion;
- pkt->completion.send.send_completion_ctx = filterPacket;
+ pkt->completion.send.send_completion_ctx = filter_pkt;
ret = netvsc_send(dev, pkt);
if (ret != 0) {
@@ -826,9 +869,9 @@ int rndis_filter_send(struct hv_device *dev,
* above
*/
pkt->completion.send.send_completion =
- filterPacket->completion;
+ filter_pkt->completion;
pkt->completion.send.send_completion_ctx =
- filterPacket->completion_ctx;
+ filter_pkt->completion_ctx;
}
return ret;
@@ -836,10 +879,10 @@ int rndis_filter_send(struct hv_device *dev,
static void rndis_filter_send_completion(void *ctx)
{
- struct rndis_filter_packet *filterPacket = ctx;
+ struct rndis_filter_packet *filter_pkt = ctx;
/* Pass it back to the original handler */
- filterPacket->completion(filterPacket->completion_ctx);
+ filter_pkt->completion(filter_pkt->completion_ctx);
}