diff options
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 | 42 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 185 |
4 files changed, 185 insertions, 79 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 1a1ca6cfc74..dd294783b5c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -123,7 +123,7 @@ static int netvsc_close(struct net_device *net) struct hv_device *device_obj = net_device_ctx->device_ctx; int ret; - netif_stop_queue(net); + netif_tx_disable(net); ret = rndis_filter_close(device_obj); if (ret != 0) @@ -151,30 +151,33 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) int ret; unsigned int i, num_pages, npg_data; - /* Add multipage for skb->data and additional one for RNDIS */ + /* Add multipages for skb->data and additional 2 for RNDIS */ npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; - num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1; + num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; /* 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)); - /* Setup the rndis header */ - packet->page_buf_cnt = num_pages; + /* If the rndis msg goes beyond 1 page, we will add 1 later */ + packet->page_buf_cnt = num_pages - 1; /* Initialize it from the skb */ packet->total_data_buflen = skb->len; @@ -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; @@ -256,7 +256,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); } else { netif_carrier_off(net); - netif_stop_queue(net); + netif_tx_disable(net); } } @@ -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,9 +293,10 @@ 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 += skb->len; + net->stats.rx_bytes += packet->total_data_buflen; /* * Pass the skb back up. Network stack will deallocate the skb when it @@ -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"); } @@ -337,7 +335,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) nvdev->start_remove = true; cancel_delayed_work_sync(&ndevctx->dwork); - netif_stop_queue(ndev); + netif_tx_disable(ndev); rndis_filter_device_remove(hdev); ndev->mtu = mtu; @@ -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, ðtool_ops); SET_NETDEV_DEV(net, &dev->device); @@ -460,7 +458,7 @@ static int netvsc_remove(struct hv_device *dev) cancel_delayed_work_sync(&ndev_ctx->dwork); /* Stop outbound asap */ - netif_stop_queue(net); + netif_tx_disable(net); unregister_netdev(net); @@ -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 da181f9a49d..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; @@ -321,10 +349,37 @@ static void rndis_filter_receive_data(struct rndis_device *dev, data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; pkt->total_data_buflen -= data_offset; + + /* + * Make sure we got a valid RNDIS message, now total_data_buflen + * should be the data packet size plus the trailer padding size + */ + if (pkt->total_data_buflen < rndis_pkt->data_len) { + netdev_err(dev->net_dev->ndev, "rndis message buffer " + "overflow detected (got %u, min %u)" + "...dropping this message!\n", + pkt->total_data_buflen, rndis_pkt->data_len); + return; + } + + /* + * Remove the rndis trailer padding from rndis packet message + * rndis_pkt->data_len tell us the real data length, we only copy + * the data packet to the stack, without the rndis trailer padding + */ + pkt->total_data_buflen = rndis_pkt->data_len; pkt->data = (void *)((unsigned long)pkt->data + data_offset); 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); } @@ -333,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) @@ -356,46 +410,32 @@ int rndis_filter_receive(struct hv_device *dev, return -ENODEV; } - rndis_hdr = pkt->data; + rndis_msg = 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)); - } - - 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; } @@ -739,53 +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 + 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) + rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT; + pkt->page_buf[1].offset = 0; + 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) { @@ -794,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; @@ -804,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); } |