]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/staging/hv/netvsc_drv.c
Staging: hv: netvsc_drv: Directly get the size of rndis_filter_packet
[mirror_ubuntu-bionic-kernel.git] / drivers / staging / hv / netvsc_drv.c
index 2d40f5f86b241964536e2dbc4d79259510817997..1b9820289175449bddb68df414557e1c42fe0c50 100644 (file)
@@ -18,6 +18,8 @@
  *   Haiyang Zhang <haiyangz@microsoft.com>
  *   Hank Janssen  <hjanssen@microsoft.com>
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/highmem.h>
 #include <net/route.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
-#include "hv_api.h"
-#include "logging.h"
-#include "version_info.h"
-#include "vmbus.h"
-#include "netvsc_api.h"
+
+#include "hyperv.h"
+#include "hyperv_net.h"
 
 struct net_device_context {
        /* point back to our device context */
        struct hv_device *device_ctx;
        unsigned long avail;
+       struct work_struct work;
 };
 
 
@@ -57,9 +58,6 @@ static int ring_size = 128;
 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 g_netvsc_drv;
-
 /* no-op so the netdev core doesn't return -EINVAL when modifying the the
  * multicast address list in SIOCADDMULTI. hv is setup to get all multicast
  * when it calls RndisFilterOnOpen() */
@@ -77,14 +75,14 @@ static int netvsc_open(struct net_device *net)
                /* Open up the device */
                ret = rndis_filter_open(device_obj);
                if (ret != 0) {
-                       DPRINT_ERR(NETVSC_DRV,
-                                  "unable to open device (ret %d).", ret);
+                       netdev_err(net, "unable to open device (ret %d).\n",
+                                  ret);
                        return ret;
                }
 
                netif_start_queue(net);
        } else {
-               DPRINT_ERR(NETVSC_DRV, "unable to open device...link is down.");
+               netdev_err(net, "unable to open device...link is down.\n");
        }
 
        return ret;
@@ -100,7 +98,7 @@ static int netvsc_close(struct net_device *net)
 
        ret = rndis_filter_close(device_obj);
        if (ret != 0)
-               DPRINT_ERR(NETVSC_DRV, "unable to close device (ret %d).", ret);
+               netdev_err(net, "unable to close device (ret %d).\n", ret);
 
        return ret;
 }
@@ -129,16 +127,10 @@ static void netvsc_xmit_completion(void *context)
 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_driver *drv =
-           drv_to_hv_drv(net_device_ctx->device_ctx->device.driver);
-       struct netvsc_driver *net_drv_obj = drv->priv;
        struct hv_netvsc_packet *packet;
        int ret;
        unsigned int i, num_pages;
 
-       DPRINT_DBG(NETVSC_DRV, "xmit packet - len %d data_len %d",
-                  skb->len, skb->data_len);
-
        /* 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)
@@ -147,10 +139,10 @@ 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)) +
-                        net_drv_obj->req_ext_size, GFP_ATOMIC);
+                        sizeof(struct rndis_filter_packet), GFP_ATOMIC);
        if (!packet) {
                /* out of memory, silently drop packet */
-               DPRINT_ERR(NETVSC_DRV, "unable to allocate hv_netvsc_packet");
+               netdev_err(net, "unable to allocate hv_netvsc_packet\n");
 
                dev_kfree_skb(skb);
                net->stats.tx_dropped++;
@@ -190,16 +182,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        packet->completion.send.send_completion_ctx = packet;
        packet->completion.send.send_completion_tid = (unsigned long)skb;
 
-       ret = net_drv_obj->send(net_device_ctx->device_ctx,
+       ret = rndis_filter_send(net_device_ctx->device_ctx,
                                  packet);
        if (ret == 0) {
                net->stats.tx_bytes += skb->len;
                net->stats.tx_packets++;
 
-               DPRINT_DBG(NETVSC_DRV, "# of xmits %lu total size %lu",
-                          net->stats.tx_packets,
-                          net->stats.tx_bytes);
-
                net_device_ctx->avail -= num_pages;
                if (net_device_ctx->avail < PACKET_PAGES_LOWATER)
                        netif_stop_queue(net);
@@ -215,14 +203,15 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
 /*
  * netvsc_linkstatus_callback - Link up/down notification
  */
-static void netvsc_linkstatus_callback(struct hv_device *device_obj,
+void netvsc_linkstatus_callback(struct hv_device *device_obj,
                                       unsigned int status)
 {
        struct net_device *net = dev_get_drvdata(&device_obj->device);
+       struct net_device_context *ndev_ctx;
 
        if (!net) {
-               DPRINT_ERR(NETVSC_DRV, "got link status but net device "
-                               "not initialized yet");
+               netdev_err(net, "got link status but net device "
+                               "not initialized yet\n");
                return;
        }
 
@@ -230,6 +219,8 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj,
                netif_carrier_on(net);
                netif_wake_queue(net);
                netif_notify_peers(net);
+               ndev_ctx = netdev_priv(net);
+               schedule_work(&ndev_ctx->work);
        } else {
                netif_carrier_off(net);
                netif_stop_queue(net);
@@ -240,7 +231,7 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj,
  * 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,
+int netvsc_recv_callback(struct hv_device *device_obj,
                                struct hv_netvsc_packet *packet)
 {
        struct net_device *net = dev_get_drvdata(&device_obj->device);
@@ -250,8 +241,8 @@ static int netvsc_recv_callback(struct hv_device *device_obj,
        unsigned long flags;
 
        if (!net) {
-               DPRINT_ERR(NETVSC_DRV, "got receive callback but net device "
-                               "not initialized yet");
+               netdev_err(net, "got receive callback but net device"
+                       " not initialized yet\n");
                return 0;
        }
 
@@ -297,9 +288,6 @@ static int netvsc_recv_callback(struct hv_device *device_obj,
         */
        netif_rx(skb);
 
-       DPRINT_DBG(NETVSC_DRV, "# of recvs %lu total size %lu",
-                  net->stats.rx_packets, net->stats.rx_bytes);
-
        return 0;
 }
 
@@ -313,8 +301,6 @@ static void netvsc_get_drvinfo(struct net_device *net,
 
 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,
 };
 
@@ -328,20 +314,32 @@ static const struct net_device_ops device_ops = {
        .ndo_set_mac_address =          eth_mac_addr,
 };
 
-static int netvsc_probe(struct device *device)
+/*
+ * Send GARP packet to network peers after migrations.
+ * After Quick Migration, the network is not immediately operational in the
+ * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add
+ * another netif_notify_peers() into a scheduled work, otherwise GARP packet
+ * will not be sent after quick migration, and cause network disconnection.
+ */
+static void netvsc_send_garp(struct work_struct *w)
+{
+       struct net_device_context *ndev_ctx;
+       struct net_device *net;
+
+       msleep(20);
+       ndev_ctx = container_of(w, struct net_device_context, work);
+       net = dev_get_drvdata(&ndev_ctx->device_ctx->device);
+       netif_notify_peers(net);
+}
+
+
+static int netvsc_probe(struct hv_device *dev)
 {
-       struct hv_driver *drv =
-               drv_to_hv_drv(device->driver);
-       struct netvsc_driver *net_drv_obj = drv->priv;
-       struct hv_device *device_obj = device_to_hv_device(device);
        struct net_device *net = NULL;
        struct net_device_context *net_device_ctx;
        struct netvsc_device_info device_info;
        int ret;
 
-       if (!net_drv_obj->base.dev_add)
-               return -1;
-
        net = alloc_etherdev(sizeof(struct net_device_context));
        if (!net)
                return -1;
@@ -350,18 +348,18 @@ static int netvsc_probe(struct device *device)
        netif_carrier_off(net);
 
        net_device_ctx = netdev_priv(net);
-       net_device_ctx->device_ctx = device_obj;
+       net_device_ctx->device_ctx = dev;
        net_device_ctx->avail = ring_size;
-       dev_set_drvdata(device, net);
+       dev_set_drvdata(&dev->device, net);
+       INIT_WORK(&net_device_ctx->work, netvsc_send_garp);
 
        /* Notify the netvsc driver of the new device */
-       ret = net_drv_obj->base.dev_add(device_obj, &device_info);
+       ret = rndis_filte_device_add(dev, &device_info);
        if (ret != 0) {
                free_netdev(net);
-               dev_set_drvdata(device, NULL);
+               dev_set_drvdata(&dev->device, NULL);
 
-               DPRINT_ERR(NETVSC_DRV, "unable to add netvsc device (ret %d)",
-                          ret);
+               netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
                return ret;
        }
 
@@ -382,38 +380,32 @@ static int netvsc_probe(struct device *device)
        net->netdev_ops = &device_ops;
 
        /* TODO: Add GSO and Checksum offload */
+       net->hw_features = NETIF_F_SG;
        net->features = NETIF_F_SG;
 
        SET_ETHTOOL_OPS(net, &ethtool_ops);
-       SET_NETDEV_DEV(net, device);
+       SET_NETDEV_DEV(net, &dev->device);
 
        ret = register_netdev(net);
        if (ret != 0) {
                /* Remove the device and release the resource */
-               net_drv_obj->base.dev_rm(device_obj);
+               rndis_filter_device_remove(dev);
                free_netdev(net);
        }
 
        return ret;
 }
 
-static int netvsc_remove(struct device *device)
+static int netvsc_remove(struct hv_device *dev)
 {
-       struct hv_driver *drv =
-               drv_to_hv_drv(device->driver);
-       struct netvsc_driver *net_drv_obj = drv->priv;
-       struct hv_device *device_obj = device_to_hv_device(device);
-       struct net_device *net = dev_get_drvdata(&device_obj->device);
+       struct net_device *net = dev_get_drvdata(&dev->device);
        int ret;
 
        if (net == NULL) {
-               DPRINT_INFO(NETVSC, "no net device to remove");
+               dev_err(&dev->device, "No net device to remove\n");
                return 0;
        }
 
-       if (!net_drv_obj->base.dev_rm)
-               return -1;
-
        /* Stop outbound asap */
        netif_stop_queue(net);
        /* netif_carrier_off(net); */
@@ -424,79 +416,40 @@ static int netvsc_remove(struct device *device)
         * Call to the vsc driver to let it know that the device is being
         * removed
         */
-       ret = net_drv_obj->base.dev_rm(device_obj);
+       ret = rndis_filter_device_remove(dev);
        if (ret != 0) {
                /* TODO: */
-               DPRINT_ERR(NETVSC, "unable to remove vsc device (ret %d)", ret);
+               netdev_err(net, "unable to remove vsc device (ret %d)\n", ret);
        }
 
        free_netdev(net);
        return ret;
 }
 
-static int netvsc_drv_exit_cb(struct device *dev, void *data)
-{
-       struct device **curr = (struct device **)data;
-
-       *curr = dev;
-       /* stop iterating */
-       return 1;
-}
+/* The one and only one */
+static struct  netvsc_driver netvsc_drv = {
+       .base.probe = netvsc_probe,
+       .base.remove = netvsc_remove,
+};
 
 static void netvsc_drv_exit(void)
 {
-       struct netvsc_driver *netvsc_drv_obj = &g_netvsc_drv;
-       struct hv_driver *drv = &g_netvsc_drv.base;
-       struct device *current_dev;
-       int ret;
-
-       while (1) {
-               current_dev = NULL;
-
-               /* Get the device */
-               ret = driver_for_each_device(&drv->driver, NULL,
-                                            &current_dev, netvsc_drv_exit_cb);
-               if (ret)
-                       DPRINT_WARN(NETVSC_DRV,
-                                   "driver_for_each_device returned %d", ret);
-
-               if (current_dev == NULL)
-                       break;
-
-               /* Initiate removal from the top-down */
-               DPRINT_INFO(NETVSC_DRV, "unregistering device (%p)...",
-                           current_dev);
-
-               device_unregister(current_dev);
-       }
-
-       if (netvsc_drv_obj->base.cleanup)
-               netvsc_drv_obj->base.cleanup(&netvsc_drv_obj->base);
-
-       vmbus_child_driver_unregister(&drv->driver);
-
-       return;
+       vmbus_child_driver_unregister(&netvsc_drv.base.driver);
 }
 
 static int netvsc_drv_init(int (*drv_init)(struct hv_driver *drv))
 {
-       struct netvsc_driver *net_drv_obj = &g_netvsc_drv;
-       struct hv_driver *drv = &g_netvsc_drv.base;
+       struct netvsc_driver *net_drv_obj = &netvsc_drv;
+       struct hv_driver *drv = &netvsc_drv.base;
        int ret;
 
        net_drv_obj->ring_buf_size = ring_size * PAGE_SIZE;
-       net_drv_obj->recv_cb = netvsc_recv_callback;
-       net_drv_obj->link_status_change = netvsc_linkstatus_callback;
-       drv->priv = net_drv_obj;
 
        /* Callback to client driver to complete the initialization */
        drv_init(&net_drv_obj->base);
 
        drv->driver.name = net_drv_obj->base.name;
 
-       drv->driver.probe = netvsc_probe;
-       drv->driver.remove = netvsc_remove;
-
        /* The driver belongs to vmbus */
        ret = vmbus_child_driver_register(&drv->driver);
 
@@ -519,7 +472,7 @@ MODULE_DEVICE_TABLE(dmi, hv_netvsc_dmi_table);
 
 static int __init netvsc_init(void)
 {
-       DPRINT_INFO(NETVSC_DRV, "Netvsc initializing....");
+       pr_info("initializing....");
 
        if (!dmi_check_system(hv_netvsc_dmi_table))
                return -ENODEV;