summaryrefslogtreecommitdiffstats
path: root/drivers/staging/hv/netvsc_drv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/hv/netvsc_drv.c')
-rw-r--r--drivers/staging/hv/netvsc_drv.c148
1 files changed, 64 insertions, 84 deletions
diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c
index 4c7739f..93b0e91 100644
--- a/drivers/staging/hv/netvsc_drv.c
+++ b/drivers/staging/hv/netvsc_drv.c
@@ -33,14 +33,11 @@
#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 "hyperv.h"
#include "hyperv_net.h"
struct net_device_context {
@@ -72,20 +69,15 @@ static int netvsc_open(struct net_device *net)
struct hv_device *device_obj = net_device_ctx->device_ctx;
int ret = 0;
- if (netif_carrier_ok(net)) {
- /* Open up the device */
- ret = rndis_filter_open(device_obj);
- if (ret != 0) {
- netdev_err(net, "unable to open device (ret %d).\n",
- ret);
- return ret;
- }
-
- netif_start_queue(net);
- } else {
- netdev_err(net, "unable to open device...link is down.\n");
+ /* Open up the device */
+ ret = rndis_filter_open(device_obj);
+ if (ret != 0) {
+ netdev_err(net, "unable to open device (ret %d).\n", ret);
+ return ret;
}
+ netif_start_queue(net);
+
return ret;
}
@@ -143,12 +135,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
(num_pages * sizeof(struct hv_page_buffer)) +
sizeof(struct rndis_filter_packet), GFP_ATOMIC);
if (!packet) {
- /* out of memory, silently drop 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;
+ return NETDEV_TX_BUSY;
}
packet->extension = (void *)(unsigned long)packet +
@@ -193,10 +185,11 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
} else {
/* we are shutting down or bus overloaded, just drop packet */
net->stats.tx_dropped++;
- netvsc_xmit_completion(packet);
+ kfree(packet);
+ dev_kfree_skb_any(skb);
}
- return NETDEV_TX_OK;
+ return ret ? NETDEV_TX_BUSY : NETDEV_TX_OK;
}
/*
@@ -205,8 +198,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
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 *net;
struct net_device_context *ndev_ctx;
+ struct netvsc_device *net_device;
+
+ net_device = hv_get_drvdata(device_obj);
+ net = net_device->ndev;
if (!net) {
netdev_err(net, "got link status but net device "
@@ -217,8 +214,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
if (status == 1) {
netif_carrier_on(net);
netif_wake_queue(net);
- netif_notify_peers(net);
ndev_ctx = netdev_priv(net);
+ schedule_delayed_work(&ndev_ctx->dwork, 0);
schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
} else {
netif_carrier_off(net);
@@ -238,6 +235,10 @@ int netvsc_recv_callback(struct hv_device *device_obj,
void *data;
int i;
unsigned long flags;
+ struct netvsc_device *net_device;
+
+ net_device = hv_get_drvdata(device_obj);
+ net = net_device->ndev;
if (!net) {
netdev_err(net, "got receive callback but net device"
@@ -324,14 +325,17 @@ static void netvsc_send_garp(struct work_struct *w)
{
struct net_device_context *ndev_ctx;
struct net_device *net;
+ struct netvsc_device *net_device;
ndev_ctx = container_of(w, struct net_device_context, dwork.work);
- net = dev_get_drvdata(&ndev_ctx->device_ctx->device);
+ net_device = hv_get_drvdata(ndev_ctx->device_ctx);
+ net = net_device->ndev;
netif_notify_peers(net);
}
-static int netvsc_probe(struct hv_device *dev)
+static int netvsc_probe(struct hv_device *dev,
+ const struct hv_vmbus_device_id *dev_id)
{
struct net_device *net = NULL;
struct net_device_context *net_device_ctx;
@@ -340,7 +344,7 @@ static int netvsc_probe(struct hv_device *dev)
net = alloc_etherdev(sizeof(struct net_device_context));
if (!net)
- return -1;
+ return -ENOMEM;
/* Set initial state */
netif_carrier_off(net);
@@ -348,24 +352,9 @@ static int netvsc_probe(struct hv_device *dev)
net_device_ctx = netdev_priv(net);
net_device_ctx->device_ctx = dev;
atomic_set(&net_device_ctx->avail, ring_size);
- dev_set_drvdata(&dev->device, net);
+ hv_set_drvdata(dev, net);
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp);
- /* Notify the netvsc driver of the new device */
- device_info.ring_size = ring_size;
- ret = rndis_filter_device_add(dev, &device_info);
- if (ret != 0) {
- free_netdev(net);
- dev_set_drvdata(&dev->device, NULL);
-
- netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
- return ret;
- }
-
- netif_carrier_on(net);
-
- memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
-
net->netdev_ops = &device_ops;
/* TODO: Add GSO and Checksum offload */
@@ -377,18 +366,37 @@ static int netvsc_probe(struct hv_device *dev)
ret = register_netdev(net);
if (ret != 0) {
- /* Remove the device and release the resource */
- rndis_filter_device_remove(dev);
+ pr_err("Unable to register netdev.\n");
+ free_netdev(net);
+ goto out;
+ }
+
+ /* Notify the netvsc driver of the new device */
+ device_info.ring_size = ring_size;
+ ret = rndis_filter_device_add(dev, &device_info);
+ if (ret != 0) {
+ netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
+ unregister_netdev(net);
free_netdev(net);
+ hv_set_drvdata(dev, NULL);
+ return ret;
}
+ memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN);
+
+ netif_carrier_on(net);
+out:
return ret;
}
static int netvsc_remove(struct hv_device *dev)
{
- struct net_device *net = dev_get_drvdata(&dev->device);
+ struct net_device *net;
struct net_device_context *ndev_ctx;
+ struct netvsc_device *net_device;
+
+ net_device = hv_get_drvdata(dev);
+ net = net_device->ndev;
if (net == NULL) {
dev_err(&dev->device, "No net device to remove\n");
@@ -413,61 +421,33 @@ static int netvsc_remove(struct hv_device *dev)
return 0;
}
+static const struct hv_vmbus_device_id id_table[] = {
+ /* Network guid */
+ { VMBUS_DEVICE(0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
+ 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E) },
+ { },
+};
+
+MODULE_DEVICE_TABLE(vmbus, id_table);
+
/* The one and only one */
static struct hv_driver netvsc_drv = {
+ .name = "netvsc",
+ .id_table = id_table,
.probe = netvsc_probe,
.remove = netvsc_remove,
};
static void __exit netvsc_drv_exit(void)
{
- vmbus_child_driver_unregister(&netvsc_drv.driver);
+ vmbus_driver_unregister(&netvsc_drv);
}
-
-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_drv_init(void)
{
- struct hv_driver *drv = &netvsc_drv;
- int ret;
-
- pr_info("initializing....");
-
- if (!dmi_check_system(hv_netvsc_dmi_table))
- return -ENODEV;
-
-
- /* Callback to client driver to complete the initialization */
- netvsc_initialize(drv);
-
- drv->driver.name = drv->name;
-
- /* The driver belongs to vmbus */
- ret = vmbus_child_driver_register(&drv->driver);
-
- return ret;
+ return vmbus_driver_register(&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_DESCRIPTION("Microsoft Hyper-V network driver");
OpenPOWER on IntegriCloud