aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/hv/netvsc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/hv/netvsc.c')
-rw-r--r--drivers/staging/hv/netvsc.c329
1 files changed, 129 insertions, 200 deletions
diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c
index dc5e5c488e3b..b902579c7fe6 100644
--- a/drivers/staging/hv/netvsc.c
+++ b/drivers/staging/hv/netvsc.c
@@ -27,122 +27,63 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/netdevice.h>
-#include "hyperv.h"
#include "hyperv_net.h"
-/* Globals */
-static const char *driver_name = "netvsc";
-
-/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
-static const struct hv_guid netvsc_device_type = {
- .data = {
- 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
- 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
- }
-};
-
-
static struct netvsc_device *alloc_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
+ struct net_device *ndev = hv_get_drvdata(device);
net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
if (!net_device)
return NULL;
- /* Set to 2 to allow both inbound and outbound traffic */
- atomic_cmpxchg(&net_device->refcnt, 0, 2);
+ net_device->destroy = false;
net_device->dev = device;
- device->ext = net_device;
+ net_device->ndev = ndev;
+ hv_set_drvdata(device, net_device);
return net_device;
}
-static void free_net_device(struct netvsc_device *device)
-{
- WARN_ON(atomic_read(&device->refcnt) != 0);
- device->dev->ext = NULL;
- kfree(device);
-}
-
-
-/* Get the net device object iff exists and its refcount > 1 */
static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
- net_device = device->ext;
- if (net_device && atomic_read(&net_device->refcnt) > 1)
- atomic_inc(&net_device->refcnt);
- else
+ net_device = hv_get_drvdata(device);
+ if (net_device && net_device->destroy)
net_device = NULL;
return net_device;
}
-/* Get the net device object iff exists and its refcount > 0 */
static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
- net_device = device->ext;
- if (net_device && atomic_read(&net_device->refcnt))
- atomic_inc(&net_device->refcnt);
- else
- net_device = NULL;
-
- return net_device;
-}
-
-static void put_net_device(struct hv_device *device)
-{
- struct netvsc_device *net_device;
-
- net_device = device->ext;
-
- atomic_dec(&net_device->refcnt);
-}
-
-static struct netvsc_device *release_outbound_net_device(
- struct hv_device *device)
-{
- struct netvsc_device *net_device;
+ net_device = hv_get_drvdata(device);
- net_device = device->ext;
- if (net_device == NULL)
- return NULL;
+ if (!net_device)
+ goto get_in_err;
- /* Busy wait until the ref drop to 2, then set it to 1 */
- while (atomic_cmpxchg(&net_device->refcnt, 2, 1) != 2)
- udelay(100);
+ if (net_device->destroy &&
+ atomic_read(&net_device->num_outstanding_sends) == 0)
+ net_device = NULL;
+get_in_err:
return net_device;
}
-static struct netvsc_device *release_inbound_net_device(
- struct hv_device *device)
-{
- struct netvsc_device *net_device;
-
- net_device = device->ext;
- if (net_device == NULL)
- return NULL;
-
- /* Busy wait until the ref drop to 1, then set it to 0 */
- while (atomic_cmpxchg(&net_device->refcnt, 1, 0) != 1)
- udelay(100);
-
- device->ext = NULL;
- return net_device;
-}
static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
{
struct nvsp_message *revoke_packet;
int ret = 0;
+ struct net_device *ndev = net_device->ndev;
/*
* If we got a section count, it means we received a
@@ -170,9 +111,9 @@ static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
* have a leak rather than continue and a bugchk
*/
if (ret != 0) {
- dev_err(&net_device->dev->device, "unable to send "
- "revoke receive buffer to netvsp");
- return -1;
+ netdev_err(ndev, "unable to send "
+ "revoke receive buffer to netvsp\n");
+ return ret;
}
}
@@ -185,9 +126,9 @@ static int netvsc_destroy_recv_buf(struct netvsc_device *net_device)
* rather than continue and a bugchk
*/
if (ret != 0) {
- dev_err(&net_device->dev->device,
- "unable to teardown receive buffer's gpadl");
- return -1;
+ netdev_err(ndev,
+ "unable to teardown receive buffer's gpadl\n");
+ return ret;
}
net_device->recv_buf_gpadl_handle = 0;
}
@@ -214,21 +155,20 @@ static int netvsc_init_recv_buf(struct hv_device *device)
int t;
struct netvsc_device *net_device;
struct nvsp_message *init_packet;
+ struct net_device *ndev;
net_device = get_outbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "unable to get net device..."
- "device being destroyed?");
- return -1;
- }
+ if (!net_device)
+ return -ENODEV;
+ ndev = net_device->ndev;
net_device->recv_buf =
(void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
get_order(net_device->recv_buf_size));
if (!net_device->recv_buf) {
- dev_err(&device->device, "unable to allocate receive "
- "buffer of size %d", net_device->recv_buf_size);
- ret = -1;
+ netdev_err(ndev, "unable to allocate receive "
+ "buffer of size %d\n", net_device->recv_buf_size);
+ ret = -ENOMEM;
goto cleanup;
}
@@ -241,8 +181,8 @@ static int netvsc_init_recv_buf(struct hv_device *device)
net_device->recv_buf_size,
&net_device->recv_buf_gpadl_handle);
if (ret != 0) {
- dev_err(&device->device,
- "unable to establish receive buffer's gpadl");
+ netdev_err(ndev,
+ "unable to establish receive buffer's gpadl\n");
goto cleanup;
}
@@ -265,8 +205,8 @@ static int netvsc_init_recv_buf(struct hv_device *device)
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (ret != 0) {
- dev_err(&device->device,
- "unable to send receive buffer's gpadl to netvsp");
+ netdev_err(ndev,
+ "unable to send receive buffer's gpadl to netvsp\n");
goto cleanup;
}
@@ -277,11 +217,11 @@ static int netvsc_init_recv_buf(struct hv_device *device)
/* Check the response */
if (init_packet->msg.v1_msg.
send_recv_buf_complete.status != NVSP_STAT_SUCCESS) {
- dev_err(&device->device, "Unable to complete receive buffer "
- "initialzation with NetVsp - status %d",
+ netdev_err(ndev, "Unable to complete receive buffer "
+ "initialization with NetVsp - status %d\n",
init_packet->msg.v1_msg.
send_recv_buf_complete.status);
- ret = -1;
+ ret = -EINVAL;
goto cleanup;
}
@@ -293,7 +233,7 @@ static int netvsc_init_recv_buf(struct hv_device *device)
net_device->recv_section = kmalloc(net_device->recv_section_cnt
* sizeof(struct nvsp_1_receive_buffer_section), GFP_KERNEL);
if (net_device->recv_section == NULL) {
- ret = -1;
+ ret = -EINVAL;
goto cleanup;
}
@@ -309,7 +249,7 @@ static int netvsc_init_recv_buf(struct hv_device *device)
*/
if (net_device->recv_section_cnt != 1 ||
net_device->recv_section->offset != 0) {
- ret = -1;
+ ret = -EINVAL;
goto cleanup;
}
@@ -319,7 +259,6 @@ cleanup:
netvsc_destroy_recv_buf(net_device);
exit:
- put_net_device(device);
return ret;
}
@@ -330,13 +269,12 @@ static int netvsc_connect_vsp(struct hv_device *device)
struct netvsc_device *net_device;
struct nvsp_message *init_packet;
int ndis_version;
+ struct net_device *ndev;
net_device = get_outbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "unable to get net device..."
- "device being destroyed?");
- return -1;
- }
+ if (!net_device)
+ return -ENODEV;
+ ndev = net_device->ndev;
init_packet = &net_device->channel_init_pkt;
@@ -366,13 +304,13 @@ static int netvsc_connect_vsp(struct hv_device *device)
if (init_packet->msg.init_msg.init_complete.status !=
NVSP_STAT_SUCCESS) {
- ret = -1;
+ ret = -EINVAL;
goto cleanup;
}
if (init_packet->msg.init_msg.init_complete.
negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) {
- ret = -1;
+ ret = -EPROTO;
goto cleanup;
}
/* Send the ndis version */
@@ -393,16 +331,13 @@ static int netvsc_connect_vsp(struct hv_device *device)
sizeof(struct nvsp_message),
(unsigned long)init_packet,
VM_PKT_DATA_INBAND, 0);
- if (ret != 0) {
- ret = -1;
+ if (ret != 0)
goto cleanup;
- }
/* Post the big receive buffer to NetVSP */
ret = netvsc_init_recv_buf(device);
cleanup:
- put_net_device(device);
return ret;
}
@@ -418,29 +353,40 @@ int netvsc_device_remove(struct hv_device *device)
{
struct netvsc_device *net_device;
struct hv_netvsc_packet *netvsc_packet, *pos;
+ unsigned long flags;
- /* Stop outbound traffic ie sends and receives completions */
- net_device = release_outbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "No net device present!!");
- return -1;
- }
+ net_device = hv_get_drvdata(device);
+ spin_lock_irqsave(&device->channel->inbound_lock, flags);
+ net_device->destroy = true;
+ spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
/* Wait for all send completions */
while (atomic_read(&net_device->num_outstanding_sends)) {
- dev_err(&device->device,
- "waiting for %d requests to complete...",
+ dev_info(&device->device,
+ "waiting for %d requests to complete...\n",
atomic_read(&net_device->num_outstanding_sends));
udelay(100);
}
netvsc_disconnect_vsp(net_device);
- /* Stop inbound traffic ie receives and sends completions */
- net_device = release_inbound_net_device(device);
+ /*
+ * Since we have already drained, we don't need to busy wait
+ * as was done in final_release_stor_device()
+ * Note that we cannot set the ext pointer to NULL until
+ * we have drained - to drain the outgoing packets, we need to
+ * allow incoming packets.
+ */
+
+ spin_lock_irqsave(&device->channel->inbound_lock, flags);
+ hv_set_drvdata(device, NULL);
+ spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
- /* At this point, no one should be accessing netDevice except in here */
- dev_notice(&device->device, "net device safe to remove");
+ /*
+ * At this point, no one should be accessing net_device
+ * except in here
+ */
+ dev_notice(&device->device, "net device safe to remove\n");
/* Now, we can close the channel safely */
vmbus_close(device->channel);
@@ -452,7 +398,7 @@ int netvsc_device_remove(struct hv_device *device)
kfree(netvsc_packet);
}
- free_net_device(net_device);
+ kfree(net_device);
return 0;
}
@@ -462,13 +408,12 @@ static void netvsc_send_completion(struct hv_device *device,
struct netvsc_device *net_device;
struct nvsp_message *nvsp_packet;
struct hv_netvsc_packet *nvsc_packet;
+ struct net_device *ndev;
net_device = get_inbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "unable to get net device..."
- "device being destroyed?");
+ if (!net_device)
return;
- }
+ ndev = net_device->ndev;
nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
(packet->offset8 << 3));
@@ -494,11 +439,10 @@ static void netvsc_send_completion(struct hv_device *device,
atomic_dec(&net_device->num_outstanding_sends);
} else {
- dev_err(&device->device, "Unknown send completion packet type- "
- "%d received!!", nvsp_packet->hdr.msg_type);
+ netdev_err(ndev, "Unknown send completion packet type- "
+ "%d received!!\n", nvsp_packet->hdr.msg_type);
}
- put_net_device(device);
}
int netvsc_send(struct hv_device *device,
@@ -506,15 +450,13 @@ int netvsc_send(struct hv_device *device,
{
struct netvsc_device *net_device;
int ret = 0;
-
struct nvsp_message sendMessage;
+ struct net_device *ndev;
net_device = get_outbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "net device (%p) shutting down..."
- "ignoring outbound packets", net_device);
- return -2;
- }
+ if (!net_device)
+ return -ENODEV;
+ ndev = net_device->ndev;
sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
if (packet->is_data_pkt) {
@@ -547,11 +489,11 @@ int netvsc_send(struct hv_device *device,
}
if (ret != 0)
- dev_err(&device->device, "Unable to send packet %p ret %d",
+ netdev_err(ndev, "Unable to send packet %p ret %d\n",
packet, ret);
+ else
+ atomic_inc(&net_device->num_outstanding_sends);
- atomic_inc(&net_device->num_outstanding_sends);
- put_net_device(device);
return ret;
}
@@ -561,6 +503,10 @@ static void netvsc_send_recv_completion(struct hv_device *device,
struct nvsp_message recvcompMessage;
int retries = 0;
int ret;
+ struct net_device *ndev;
+ struct netvsc_device *net_device = hv_get_drvdata(device);
+
+ ndev = net_device->ndev;
recvcompMessage.hdr.msg_type =
NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE;
@@ -577,23 +523,23 @@ retry_send_cmplt:
if (ret == 0) {
/* success */
/* no-op */
- } else if (ret == -1) {
+ } else if (ret == -EAGAIN) {
/* no more room...wait a bit and attempt to retry 3 times */
retries++;
- dev_err(&device->device, "unable to send receive completion pkt"
- " (tid %llx)...retrying %d", transaction_id, retries);
+ netdev_err(ndev, "unable to send receive completion pkt"
+ " (tid %llx)...retrying %d\n", transaction_id, retries);
if (retries < 4) {
udelay(100);
goto retry_send_cmplt;
} else {
- dev_err(&device->device, "unable to send receive "
- "completion pkt (tid %llx)...give up retrying",
+ netdev_err(ndev, "unable to send receive "
+ "completion pkt (tid %llx)...give up retrying\n",
transaction_id);
}
} else {
- dev_err(&device->device, "unable to send receive "
- "completion pkt - %llx", transaction_id);
+ netdev_err(ndev, "unable to send receive "
+ "completion pkt - %llx\n", transaction_id);
}
}
@@ -606,6 +552,7 @@ static void netvsc_receive_completion(void *context)
u64 transaction_id = 0;
bool fsend_receive_comp = false;
unsigned long flags;
+ struct net_device *ndev;
/*
* Even though it seems logical to do a GetOutboundNetDevice() here to
@@ -613,11 +560,9 @@ static void netvsc_receive_completion(void *context)
* since we may have disable outbound traffic already.
*/
net_device = get_inbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "unable to get net device..."
- "device being destroyed?");
+ if (!net_device)
return;
- }
+ ndev = net_device->ndev;
/* Overloading use of the lock. */
spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags);
@@ -644,7 +589,6 @@ static void netvsc_receive_completion(void *context)
if (fsend_receive_comp)
netvsc_send_recv_completion(device, transaction_id);
- put_net_device(device);
}
static void netvsc_receive(struct hv_device *device,
@@ -661,24 +605,22 @@ static void netvsc_receive(struct hv_device *device,
int i, j;
int count = 0, bytes_remain = 0;
unsigned long flags;
+ struct net_device *ndev;
LIST_HEAD(listHead);
net_device = get_inbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "unable to get net device..."
- "device being destroyed?");
+ if (!net_device)
return;
- }
+ ndev = net_device->ndev;
/*
* All inbound packets other than send completion should be xfer page
* packet
*/
if (packet->type != VM_PKT_DATA_USING_XFER_PAGES) {
- dev_err(&device->device, "Unknown packet type received - %d",
+ netdev_err(ndev, "Unknown packet type received - %d\n",
packet->type);
- put_net_device(device);
return;
}
@@ -688,19 +630,17 @@ static void netvsc_receive(struct hv_device *device,
/* Make sure this is a valid nvsp packet */
if (nvsp_packet->hdr.msg_type !=
NVSP_MSG1_TYPE_SEND_RNDIS_PKT) {
- dev_err(&device->device, "Unknown nvsp packet type received-"
- " %d", nvsp_packet->hdr.msg_type);
- put_net_device(device);
+ netdev_err(ndev, "Unknown nvsp packet type received-"
+ " %d\n", nvsp_packet->hdr.msg_type);
return;
}
vmxferpage_packet = (struct vmtransfer_page_packet_header *)packet;
if (vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID) {
- dev_err(&device->device, "Invalid xfer page set id - "
- "expecting %x got %x", NETVSC_RECEIVE_BUFFER_ID,
+ netdev_err(ndev, "Invalid xfer page set id - "
+ "expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID,
vmxferpage_packet->xfer_pageset_id);
- put_net_device(device);
return;
}
@@ -724,8 +664,8 @@ static void netvsc_receive(struct hv_device *device,
* some of the xfer page packet ranges...
*/
if (count < 2) {
- dev_err(&device->device, "Got only %d netvsc pkt...needed "
- "%d pkts. Dropping this xfer page packet completely!",
+ netdev_err(ndev, "Got only %d netvsc pkt...needed "
+ "%d pkts. Dropping this xfer page packet completely!\n",
count, vmxferpage_packet->range_cnt + 1);
/* Return it to the freelist */
@@ -740,7 +680,6 @@ static void netvsc_receive(struct hv_device *device,
netvsc_send_recv_completion(device,
vmxferpage_packet->d.trans_id);
- put_net_device(device);
return;
}
@@ -752,8 +691,8 @@ static void netvsc_receive(struct hv_device *device,
xferpage_packet->count = count - 1;
if (xferpage_packet->count != vmxferpage_packet->range_cnt) {
- dev_err(&device->device, "Needed %d netvsc pkts to satisy "
- "this xfer page...got %d",
+ netdev_err(ndev, "Needed %d netvsc pkts to satisfy "
+ "this xfer page...got %d\n",
vmxferpage_packet->range_cnt, xferpage_packet->count);
}
@@ -828,7 +767,6 @@ static void netvsc_receive(struct hv_device *device,
completion.recv.recv_completion_ctx);
}
- put_net_device(device);
}
static void netvsc_channel_cb(void *context)
@@ -842,6 +780,7 @@ static void netvsc_channel_cb(void *context)
struct vmpacket_descriptor *desc;
unsigned char *buffer;
int bufferlen = NETVSC_PACKET_SIZE;
+ struct net_device *ndev;
packet = kzalloc(NETVSC_PACKET_SIZE * sizeof(unsigned char),
GFP_ATOMIC);
@@ -850,11 +789,9 @@ static void netvsc_channel_cb(void *context)
buffer = packet;
net_device = get_inbound_net_device(device);
- if (!net_device) {
- dev_err(&device->device, "net device (%p) shutting down..."
- "ignoring inbound packets", net_device);
+ if (!net_device)
goto out;
- }
+ ndev = net_device->ndev;
do {
ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen,
@@ -872,7 +809,7 @@ static void netvsc_channel_cb(void *context)
break;
default:
- dev_err(&device->device,
+ netdev_err(ndev,
"unhandled packet type %d, "
"tid %llx len %d\n",
desc->type, request_id,
@@ -896,14 +833,14 @@ static void netvsc_channel_cb(void *context)
break;
}
- } else if (ret == -2) {
+ } else if (ret == -ENOBUFS) {
/* Handle large packet */
buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
if (buffer == NULL) {
/* Try again next time around */
- dev_err(&device->device,
+ netdev_err(ndev,
"unable to allocate buffer of size "
- "(%d)!!", bytes_recvd);
+ "(%d)!!\n", bytes_recvd);
break;
}
@@ -911,7 +848,6 @@ static void netvsc_channel_cb(void *context)
}
} while (1);
- put_net_device(device);
out:
kfree(buffer);
return;
@@ -929,13 +865,23 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
((struct netvsc_device_info *)additional_info)->ring_size;
struct netvsc_device *net_device;
struct hv_netvsc_packet *packet, *pos;
+ struct net_device *ndev;
net_device = alloc_net_device(device);
if (!net_device) {
- ret = -1;
+ ret = -ENOMEM;
goto cleanup;
}
+ /*
+ * Coming into this function, struct net_device * is
+ * registered as the driver private data.
+ * In alloc_net_device(), we register struct netvsc_device *
+ * as the driver private data and stash away struct net_device *
+ * in struct netvsc_device *.
+ */
+ ndev = net_device->ndev;
+
/* Initialize the NetVSC channel extension */
net_device->recv_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
spin_lock_init(&net_device->recv_pkt_list_lock);
@@ -960,20 +906,18 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
netvsc_channel_cb, device);
if (ret != 0) {
- dev_err(&device->device, "unable to open channel: %d", ret);
- ret = -1;
+ netdev_err(ndev, "unable to open channel: %d\n", ret);
goto cleanup;
}
/* Channel is opened */
- pr_info("hv_netvsc channel opened successfully");
+ pr_info("hv_netvsc channel opened successfully\n");
/* Connect with the NetVsp */
ret = netvsc_connect_vsp(device);
if (ret != 0) {
- dev_err(&device->device,
- "unable to connect to NetVSP - %d", ret);
- ret = -1;
+ netdev_err(ndev,
+ "unable to connect to NetVSP - %d\n", ret);
goto close;
}
@@ -993,23 +937,8 @@ cleanup:
kfree(packet);
}
- release_outbound_net_device(device);
- release_inbound_net_device(device);
-
- free_net_device(net_device);
+ kfree(net_device);
}
return ret;
}
-
-/*
- * netvsc_initialize - Main entry point
- */
-int netvsc_initialize(struct hv_driver *drv)
-{
-
- drv->name = driver_name;
- memcpy(&drv->dev_type, &netvsc_device_type, sizeof(struct hv_guid));
-
- return 0;
-}