diff options
Diffstat (limited to 'drivers/net/hyperv/netvsc_drv.c')
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 125 |
1 files changed, 87 insertions, 38 deletions
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 15d82eda0baf..5993c7e2d723 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -40,18 +40,21 @@ #include "hyperv_net.h" -struct net_device_context { - /* point back to our device context */ - struct hv_device *device_ctx; - struct delayed_work dwork; - struct work_struct work; -}; #define RING_SIZE_MIN 64 static int ring_size = 128; module_param(ring_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); +static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | + NETIF_MSG_LINK | NETIF_MSG_IFUP | + NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR | + NETIF_MSG_TX_ERR; + +static int debug = -1; +module_param(debug, int, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + static void do_set_multicast(struct work_struct *w) { struct net_device_context *ndevctx = @@ -229,16 +232,13 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, return q_idx; } -static void netvsc_xmit_completion(void *context) +void netvsc_xmit_completion(void *context) { struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; struct sk_buff *skb = (struct sk_buff *) (unsigned long)packet->send_completion_tid; - u32 index = packet->send_buf_index; - kfree(packet); - - if (skb && (index == NETVSC_INVALID_INDEX)) + if (skb) dev_kfree_skb_any(skb); } @@ -277,15 +277,16 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, } static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, - struct hv_page_buffer *pb) + struct hv_netvsc_packet *packet) { + struct hv_page_buffer *pb = packet->page_buf; u32 slots_used = 0; char *data = skb->data; int frags = skb_shinfo(skb)->nr_frags; int i; /* The packet is laid out thus: - * 1. hdr + * 1. hdr: RNDIS header and PPI * 2. skb linear data * 3. skb fragment data */ @@ -294,6 +295,9 @@ static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, offset_in_page(hdr), len, &pb[slots_used]); + packet->rmsg_size = len; + packet->rmsg_pgcnt = slots_used; + slots_used += fill_pg_buf(virt_to_page(data), offset_in_page(data), skb_headlen(skb), &pb[slots_used]); @@ -370,50 +374,65 @@ not_ip: static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct hv_netvsc_packet *packet; + struct hv_netvsc_packet *packet = NULL; int ret; unsigned int num_data_pgs; struct rndis_message *rndis_msg; struct rndis_packet *rndis_pkt; u32 rndis_msg_size; bool isvlan; + bool linear = false; struct rndis_per_packet_info *ppi; struct ndis_tcp_ip_checksum_info *csum_info; struct ndis_tcp_lso_info *lso_info; int hdr_offset; u32 net_trans_info; u32 hash; - u32 skb_length = skb->len; + u32 skb_length; + u32 pkt_sz; + struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; /* We will atmost need two pages to describe the rndis * header. We can only transmit MAX_PAGE_BUFFER_COUNT number - * of pages in a single packet. + * of pages in a single packet. If skb is scattered around + * more pages we try linearizing it. */ + +check_size: + skb_length = skb->len; num_data_pgs = netvsc_get_slots(skb) + 2; - if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { - netdev_err(net, "Packet too big: %u\n", skb->len); - dev_kfree_skb(skb); - net->stats.tx_dropped++; - return NETDEV_TX_OK; + if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) { + net_alert_ratelimited("packet too big: %u pages (%u bytes)\n", + num_data_pgs, skb->len); + ret = -EFAULT; + goto drop; + } else if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { + if (skb_linearize(skb)) { + net_alert_ratelimited("failed to linearize skb\n"); + ret = -ENOMEM; + goto drop; + } + linear = true; + goto check_size; } - /* Allocate a netvsc packet based on # of frags. */ - packet = kzalloc(sizeof(struct hv_netvsc_packet) + - (num_data_pgs * sizeof(struct hv_page_buffer)) + - sizeof(struct rndis_message) + - NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + - NDIS_LSO_PPI_SIZE + NDIS_HASH_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_OK; + pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE; + + ret = skb_cow_head(skb, pkt_sz); + if (ret) { + netdev_err(net, "unable to alloc hv_netvsc_packet\n"); + ret = -ENOMEM; + goto drop; } + /* Use the headroom for building up the packet */ + packet = (struct hv_netvsc_packet *)skb->head; + + packet->status = 0; + packet->xmit_more = skb->xmit_more; packet->vlan_tci = skb->vlan_tci; + packet->page_buf = page_buf; packet->q_idx = skb_get_queue_mapping(skb); @@ -421,8 +440,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) packet->total_data_buflen = skb->len; packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + - sizeof(struct hv_netvsc_packet) + - (num_data_pgs * sizeof(struct hv_page_buffer))); + sizeof(struct hv_netvsc_packet)); + + memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE); /* Set the completion routine */ packet->send_completion = netvsc_xmit_completion; @@ -554,7 +574,7 @@ do_send: rndis_msg->msg_len += rndis_msg_size; packet->total_data_buflen = rndis_msg->msg_len; packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, - skb, &packet->page_buf[0]); + skb, packet); ret = netvsc_send(net_device_ctx->device_ctx, packet); @@ -563,7 +583,6 @@ drop: net->stats.tx_bytes += skb_length; net->stats.tx_packets++; } else { - kfree(packet); if (ret != -EAGAIN) { dev_kfree_skb_any(skb); net->stats.tx_dropped++; @@ -687,6 +706,19 @@ static void netvsc_get_drvinfo(struct net_device *net, strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); } +static void netvsc_get_channels(struct net_device *net, + struct ethtool_channels *channel) +{ + struct net_device_context *net_device_ctx = netdev_priv(net); + struct hv_device *dev = net_device_ctx->device_ctx; + struct netvsc_device *nvdev = hv_get_drvdata(dev); + + if (nvdev) { + channel->max_combined = nvdev->max_chn; + channel->combined_count = nvdev->num_chn; + } +} + static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); @@ -760,6 +792,7 @@ static void netvsc_poll_controller(struct net_device *net) static const struct ethtool_ops ethtool_ops = { .get_drvinfo = netvsc_get_drvinfo, .get_link = ethtool_op_get_link, + .get_channels = netvsc_get_channels, }; static const struct net_device_ops device_ops = { @@ -831,16 +864,25 @@ static int netvsc_probe(struct hv_device *dev, struct netvsc_device_info device_info; struct netvsc_device *nvdev; int ret; + u32 max_needed_headroom; net = alloc_etherdev_mq(sizeof(struct net_device_context), num_online_cpus()); if (!net) return -ENOMEM; + max_needed_headroom = sizeof(struct hv_netvsc_packet) + + RNDIS_AND_PPI_SIZE; + netif_carrier_off(net); net_device_ctx = netdev_priv(net); net_device_ctx->device_ctx = dev; + net_device_ctx->msg_enable = netif_msg_init(debug, default_msg); + if (netif_msg_probe(net_device_ctx)) + netdev_dbg(net, "netvsc msg_enable: %d\n", + net_device_ctx->msg_enable); + hv_set_drvdata(dev, net); INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); INIT_WORK(&net_device_ctx->work, do_set_multicast); @@ -855,6 +897,13 @@ static int netvsc_probe(struct hv_device *dev, net->ethtool_ops = ðtool_ops; SET_NETDEV_DEV(net, &dev->device); + /* + * Request additional head room in the skb. + * We will use this space to build the rndis + * heaser and other state we need to maintain. + */ + net->needed_headroom = max_needed_headroom; + /* Notify the netvsc driver of the new device */ device_info.ring_size = ring_size; ret = rndis_filter_device_add(dev, &device_info); |