Merge staging-next tree into Linus's latest version
[pandora-kernel.git] / drivers / staging / hv / netvsc_drv.c
index ab27d9a..55b9932 100644 (file)
 #include <linux/skbuff.h>
 #include <linux/in.h>
 #include <linux/slab.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
 #include <net/arp.h>
 #include <net/route.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
 #include "osd.h"
 #include "logging.h"
-#include "VersionInfo.h"
+#include "version_info.h"
 #include "vmbus.h"
-#include "NetVscApi.h"
+#include "netvsc_api.h"
 
 struct net_device_context {
        /* point back to our device context */
        struct vm_device *device_ctx;
-       struct net_device_stats stats;
+       unsigned long avail;
 };
 
 struct netvsc_driver_context {
@@ -53,18 +55,17 @@ struct netvsc_driver_context {
        struct netvsc_driver drv_obj;
 };
 
-static int netvsc_ringbuffer_size = NETVSC_DEVICE_RING_BUFFER_SIZE;
+#define PACKET_PAGES_LOWATER  8
+/* Need this many pages to handle worst case fragmented packet */
+#define PACKET_PAGES_HIWATER  (MAX_SKB_FRAGS + 2)
+
+static int ring_size = roundup_pow_of_two(2*MAX_SKB_FRAGS+1);
+module_param(ring_size, int, S_IRUGO);
+MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
 
 /* The one and only one */
 static struct netvsc_driver_context g_netvsc_drv;
 
-static struct net_device_stats *netvsc_get_stats(struct net_device *net)
-{
-       struct net_device_context *net_device_ctx = netdev_priv(net);
-
-       return &net_device_ctx->stats;
-}
-
 static void netvsc_set_multicast_list(struct net_device *net)
 {
 }
@@ -78,9 +79,6 @@ static int netvsc_open(struct net_device *net)
        DPRINT_ENTER(NETVSC_DRV);
 
        if (netif_carrier_ok(net)) {
-               memset(&net_device_ctx->stats, 0,
-                      sizeof(struct net_device_stats));
-
                /* Open up the device */
                ret = RndisFilterOnOpen(device_obj);
                if (ret != 0) {
@@ -122,22 +120,20 @@ static 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->Completion.Send.SendCompletionTid;
-       struct net_device *net;
 
        DPRINT_ENTER(NETVSC_DRV);
 
        kfree(packet);
 
        if (skb) {
-               net = skb->dev;
-               dev_kfree_skb_any(skb);
+               struct net_device *net = skb->dev;
+               struct net_device_context *net_device_ctx = netdev_priv(net);
+               unsigned int num_pages = skb_shinfo(skb)->nr_frags + 2;
 
-               if (netif_queue_stopped(net)) {
-                       DPRINT_INFO(NETVSC_DRV, "net device (%p) waking up...",
-                                   net);
+               dev_kfree_skb_any(skb);
 
-                       netif_wake_queue(net);
-               }
+               if ((net_device_ctx->avail += num_pages) >= PACKET_PAGES_HIWATER)
+                       netif_wake_queue(net);
        }
 
        DPRINT_EXIT(NETVSC_DRV);
@@ -152,65 +148,58 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                (struct netvsc_driver_context *)driver_ctx;
        struct netvsc_driver *net_drv_obj = &net_drv_ctx->drv_obj;
        struct hv_netvsc_packet *packet;
-       int i;
        int ret;
-       int num_frags;
-       int retries = 0;
+       unsigned int i, num_pages;
 
        DPRINT_ENTER(NETVSC_DRV);
 
-       /* Support only 1 chain of frags */
-       ASSERT(skb_shinfo(skb)->frag_list == NULL);
-       ASSERT(skb->dev == net);
-
        DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d",
                   skb->len, skb->data_len);
 
-       /* Add 1 for skb->data and any additional ones requested */
-       num_frags = skb_shinfo(skb)->nr_frags + 1 +
-                   net_drv_obj->AdditionalRequestPageBufferCount;
+       /* Add 1 for skb->data and additional one for RNDIS */
+       num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
+       if (num_pages > net_device_ctx->avail)
+               return NETDEV_TX_BUSY;
 
        /* Allocate a netvsc packet based on # of frags. */
        packet = kzalloc(sizeof(struct hv_netvsc_packet) +
-                        (num_frags * sizeof(struct hv_page_buffer)) +
+                        (num_pages * sizeof(struct hv_page_buffer)) +
                         net_drv_obj->RequestExtSize, GFP_ATOMIC);
        if (!packet) {
+               /* out of memory, silently drop packet */
                DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet");
-               return -1;
+
+               dev_kfree_skb(skb);
+               net->stats.tx_dropped++;
+               return NETDEV_TX_OK;
        }
 
        packet->Extension = (void *)(unsigned long)packet +
                                sizeof(struct hv_netvsc_packet) +
-                                   (num_frags * sizeof(struct hv_page_buffer));
+                                   (num_pages * sizeof(struct hv_page_buffer));
 
        /* Setup the rndis header */
-       packet->PageBufferCount = num_frags;
+       packet->PageBufferCount = num_pages;
 
        /* TODO: Flush all write buffers/ memory fence ??? */
        /* wmb(); */
 
        /* Initialize it from the skb */
-       ASSERT(skb->data);
        packet->TotalDataBufferLength   = skb->len;
 
-       /*
-        * Start filling in the page buffers starting at
-        * AdditionalRequestPageBufferCount offset
-        */
-       packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
-       packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Offset = (unsigned long)skb->data & (PAGE_SIZE - 1);
-       packet->PageBuffers[net_drv_obj->AdditionalRequestPageBufferCount].Length = skb->len - skb->data_len;
-
-       ASSERT((skb->len - skb->data_len) <= PAGE_SIZE);
-
-       for (i = net_drv_obj->AdditionalRequestPageBufferCount + 1;
-            i < num_frags; i++) {
-               packet->PageBuffers[i].Pfn =
-                       page_to_pfn(skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page);
-               packet->PageBuffers[i].Offset =
-                       skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].page_offset;
-               packet->PageBuffers[i].Length =
-                       skb_shinfo(skb)->frags[i-(net_drv_obj->AdditionalRequestPageBufferCount+1)].size;
+       /* Start filling in the page buffers starting after RNDIS buffer. */
+       packet->PageBuffers[1].Pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
+       packet->PageBuffers[1].Offset
+               = (unsigned long)skb->data & (PAGE_SIZE - 1);
+       packet->PageBuffers[1].Length = skb_headlen(skb);
+
+       /* Additional fragments are after SKB data */
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               skb_frag_t *f = &skb_shinfo(skb)->frags[i];
+
+               packet->PageBuffers[i+2].Pfn = page_to_pfn(f->page);
+               packet->PageBuffers[i+2].Offset = f->page_offset;
+               packet->PageBuffers[i+2].Length = f->size;
        }
 
        /* Set the completion routine */
@@ -218,55 +207,29 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        packet->Completion.Send.SendCompletionContext = packet;
        packet->Completion.Send.SendCompletionTid = (unsigned long)skb;
 
-retry_send:
        ret = net_drv_obj->OnSend(&net_device_ctx->device_ctx->device_obj,
                                  packet);
-
        if (ret == 0) {
-               ret = NETDEV_TX_OK;
-               net_device_ctx->stats.tx_bytes += skb->len;
-               net_device_ctx->stats.tx_packets++;
-       } else {
-               retries++;
-               if (retries < 4) {
-                       DPRINT_ERR(NETVSC_DRV, "unable to send..."
-                                       "retrying %d...", retries);
-                       udelay(100);
-                       goto retry_send;
-               }
-
-               /* no more room or we are shutting down */
-               DPRINT_ERR(NETVSC_DRV, "unable to send (%d)..."
-                          "marking net device (%p) busy", ret, net);
-               DPRINT_INFO(NETVSC_DRV, "net device (%p) stopping", net);
+               net->stats.tx_bytes += skb->len;
+               net->stats.tx_packets++;
 
-               ret = NETDEV_TX_BUSY;
-               net_device_ctx->stats.tx_dropped++;
+               DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu",
+                          net->stats.tx_packets,
+                          net->stats.tx_bytes);
 
-               netif_stop_queue(net);
-
-               /*
-                * Null it since the caller will free it instead of the
-                * completion routine
-                */
-               packet->Completion.Send.SendCompletionTid = 0;
-
-               /*
-                * Release the resources since we will not get any send
-                * completion
-                */
-               netvsc_xmit_completion((void *)packet);
+               if ((net_device_ctx->avail -= num_pages) < PACKET_PAGES_LOWATER)
+                       netif_stop_queue(net);
+       } else {
+               /* we are shutting down or bus overloaded, just drop packet */
+               net->stats.tx_dropped++;
+               netvsc_xmit_completion(packet);
        }
 
-       DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu",
-                  net_device_ctx->stats.tx_packets,
-                  net_device_ctx->stats.tx_bytes);
-
        DPRINT_EXIT(NETVSC_DRV);
-       return ret;
+       return NETDEV_TX_OK;
 }
 
-/**
+/*
  * netvsc_linkstatus_callback - Link up/down notification
  */
 static void netvsc_linkstatus_callback(struct hv_device *device_obj,
@@ -293,18 +256,17 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj,
        DPRINT_EXIT(NETVSC_DRV);
 }
 
-/**
- * netvsc_recv_callback -  Callback when we receive a packet from the "wire" on the specified device.
+/*
+ * netvsc_recv_callback -  Callback when we receive a packet from the
+ * "wire" on the specified device.
  */
 static int netvsc_recv_callback(struct hv_device *device_obj,
                                struct hv_netvsc_packet *packet)
 {
        struct vm_device *device_ctx = to_vm_device(device_obj);
        struct net_device *net = dev_get_drvdata(&device_ctx->device);
-       struct net_device_context *net_device_ctx;
        struct sk_buff *skb;
        void *data;
-       int ret;
        int i;
        unsigned long flags;
 
@@ -316,14 +278,12 @@ static int netvsc_recv_callback(struct hv_device *device_obj,
                return 0;
        }
 
-       net_device_ctx = netdev_priv(net);
-
-       /* Allocate a skb - TODO preallocate this */
-       /* Pad 2-bytes to align IP header to 16 bytes */
-       skb = dev_alloc_skb(packet->TotalDataBufferLength + 2);
-       ASSERT(skb);
-       skb_reserve(skb, 2);
-       skb->dev = net;
+       /* Allocate a skb - TODO direct I/O to pages? */
+       skb = netdev_alloc_skb_ip_align(net, packet->TotalDataBufferLength);
+       if (unlikely(!skb)) {
+               ++net->stats.rx_dropped;
+               return 0;
+       }
 
        /* for kmap_atomic */
        local_irq_save(flags);
@@ -348,39 +308,45 @@ static int netvsc_recv_callback(struct hv_device *device_obj,
        local_irq_restore(flags);
 
        skb->protocol = eth_type_trans(skb, net);
-
        skb->ip_summed = CHECKSUM_NONE;
 
+       net->stats.rx_packets++;
+       net->stats.rx_bytes += skb->len;
+
        /*
         * Pass the skb back up. Network stack will deallocate the skb when it
-        * is done
+        * is done.
+        * TODO - use NAPI?
         */
-       ret = netif_rx(skb);
-
-       switch (ret) {
-       case NET_RX_DROP:
-               net_device_ctx->stats.rx_dropped++;
-               break;
-       default:
-               net_device_ctx->stats.rx_packets++;
-               net_device_ctx->stats.rx_bytes += skb->len;
-               break;
+       netif_rx(skb);
 
-       }
        DPRINT_DBG(NETVSC_DRV, "# of recvs %lu total size %lu",
-                  net_device_ctx->stats.rx_packets,
-                  net_device_ctx->stats.rx_bytes);
+                  net->stats.rx_packets, net->stats.rx_bytes);
 
        DPRINT_EXIT(NETVSC_DRV);
 
        return 0;
 }
 
+static void netvsc_get_drvinfo(struct net_device *net,
+                              struct ethtool_drvinfo *info)
+{
+       strcpy(info->driver, "hv_netvsc");
+       strcpy(info->version, HV_DRV_VERSION);
+       strcpy(info->fw_version, "N/A");
+}
+
+static const struct ethtool_ops ethtool_ops = {
+       .get_drvinfo    = netvsc_get_drvinfo,
+       .get_sg         = ethtool_op_get_sg,
+       .set_sg         = ethtool_op_set_sg,
+       .get_link       = ethtool_op_get_link,
+};
+
 static const struct net_device_ops device_ops = {
        .ndo_open =                     netvsc_open,
        .ndo_stop =                     netvsc_close,
        .ndo_start_xmit =               netvsc_start_xmit,
-       .ndo_get_stats =                netvsc_get_stats,
        .ndo_set_multicast_list =       netvsc_set_multicast_list,
 };
 
@@ -413,6 +379,7 @@ static int netvsc_probe(struct device *device)
 
        net_device_ctx = netdev_priv(net);
        net_device_ctx->device_ctx = device_ctx;
+       net_device_ctx->avail = ring_size;
        dev_set_drvdata(device, net);
 
        /* Notify the netvsc driver of the new device */
@@ -442,6 +409,10 @@ static int netvsc_probe(struct device *device)
 
        net->netdev_ops = &device_ops;
 
+       /* TODO: Add GSO and Checksum offload */
+       net->features = NETIF_F_SG;
+
+       SET_ETHTOOL_OPS(net, &ethtool_ops);
        SET_NETDEV_DEV(net, device);
 
        ret = register_netdev(net);
@@ -559,7 +530,7 @@ static int netvsc_drv_init(int (*drv_init)(struct hv_driver *drv))
 
        vmbus_get_interface(&net_drv_obj->Base.VmbusChannelInterface);
 
-       net_drv_obj->RingBufferSize = netvsc_ringbuffer_size;
+       net_drv_obj->RingBufferSize = ring_size * PAGE_SIZE;
        net_drv_obj->OnReceiveCallback = netvsc_recv_callback;
        net_drv_obj->OnLinkStatusChanged = netvsc_linkstatus_callback;
 
@@ -581,6 +552,20 @@ static int netvsc_drv_init(int (*drv_init)(struct hv_driver *drv))
        return ret;
 }
 
+static const struct dmi_system_id __initconst
+hv_netvsc_dmi_table[] __maybe_unused  = {
+       {
+               .ident = "Hyper-V",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
+               },
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(dmi, hv_netvsc_dmi_table);
+
 static int __init netvsc_init(void)
 {
        int ret;
@@ -588,6 +573,9 @@ static int __init netvsc_init(void)
        DPRINT_ENTER(NETVSC_DRV);
        DPRINT_INFO(NETVSC_DRV, "Netvsc initializing....");
 
+       if (!dmi_check_system(hv_netvsc_dmi_table))
+               return -ENODEV;
+
        ret = netvsc_drv_init(NetVscInitialize);
 
        DPRINT_EXIT(NETVSC_DRV);
@@ -602,9 +590,16 @@ static void __exit netvsc_exit(void)
        DPRINT_EXIT(NETVSC_DRV);
 }
 
+static const struct pci_device_id __initconst
+hv_netvsc_pci_table[] __maybe_unused = {
+       { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, hv_netvsc_pci_table);
+
 MODULE_LICENSE("GPL");
 MODULE_VERSION(HV_DRV_VERSION);
-module_param(netvsc_ringbuffer_size, int, S_IRUGO);
+MODULE_DESCRIPTION("Microsoft Hyper-V network driver");
 
 module_init(netvsc_init);
 module_exit(netvsc_exit);