aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/hyperv/netvsc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/hyperv/netvsc.c')
-rw-r--r--drivers/net/hyperv/netvsc.c329
1 files changed, 215 insertions, 114 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 410fb8e81376..ff05b9b0837f 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -59,7 +59,6 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf)
VM_PKT_DATA_INBAND, 0);
}
-
static struct netvsc_device *alloc_net_device(void)
{
struct netvsc_device *net_device;
@@ -74,17 +73,26 @@ static struct netvsc_device *alloc_net_device(void)
return NULL;
}
+ net_device->mrc[0].buf = vzalloc(NETVSC_RECVSLOT_MAX *
+ sizeof(struct recv_comp_data));
+
init_waitqueue_head(&net_device->wait_drain);
net_device->destroy = false;
atomic_set(&net_device->open_cnt, 0);
net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
+ init_completion(&net_device->channel_init_wait);
return net_device;
}
static void free_netvsc_device(struct netvsc_device *nvdev)
{
+ int i;
+
+ for (i = 0; i < VRSS_CHANNEL_MAX; i++)
+ vfree(nvdev->mrc[i].buf);
+
kfree(nvdev->cb_buffer);
kfree(nvdev);
}
@@ -107,20 +115,20 @@ static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
goto get_in_err;
if (net_device->destroy &&
- atomic_read(&net_device->num_outstanding_sends) == 0)
+ atomic_read(&net_device->num_outstanding_sends) == 0 &&
+ atomic_read(&net_device->num_outstanding_recvs) == 0)
net_device = NULL;
get_in_err:
return net_device;
}
-
-static int netvsc_destroy_buf(struct hv_device *device)
+static void netvsc_destroy_buf(struct hv_device *device)
{
struct nvsp_message *revoke_packet;
- int ret = 0;
struct net_device *ndev = hv_get_drvdata(device);
struct netvsc_device *net_device = net_device_to_netvsc_device(ndev);
+ int ret;
/*
* If we got a section count, it means we received a
@@ -150,7 +158,7 @@ static int netvsc_destroy_buf(struct hv_device *device)
if (ret != 0) {
netdev_err(ndev, "unable to send "
"revoke receive buffer to netvsp\n");
- return ret;
+ return;
}
}
@@ -165,7 +173,7 @@ static int netvsc_destroy_buf(struct hv_device *device)
if (ret != 0) {
netdev_err(ndev,
"unable to teardown receive buffer's gpadl\n");
- return ret;
+ return;
}
net_device->recv_buf_gpadl_handle = 0;
}
@@ -209,7 +217,7 @@ static int netvsc_destroy_buf(struct hv_device *device)
if (ret != 0) {
netdev_err(ndev, "unable to send "
"revoke send buffer to netvsp\n");
- return ret;
+ return;
}
}
/* Teardown the gpadl on the vsp end */
@@ -223,7 +231,7 @@ static int netvsc_destroy_buf(struct hv_device *device)
if (ret != 0) {
netdev_err(ndev,
"unable to teardown send buffer's gpadl\n");
- return ret;
+ return;
}
net_device->send_buf_gpadl_handle = 0;
}
@@ -233,8 +241,6 @@ static int netvsc_destroy_buf(struct hv_device *device)
net_device->send_buf = NULL;
}
kfree(net_device->send_section_map);
-
- return ret;
}
static int netvsc_init_buf(struct hv_device *device)
@@ -276,7 +282,6 @@ static int netvsc_init_buf(struct hv_device *device)
goto cleanup;
}
-
/* Notify the NetVsp of the gpadl handle */
init_packet = &net_device->channel_init_pkt;
@@ -403,7 +408,7 @@ static int netvsc_init_buf(struct hv_device *device)
/* Section count is simply the size divided by the section size.
*/
net_device->send_section_cnt =
- net_device->send_buf_size/net_device->send_section_size;
+ net_device->send_buf_size / net_device->send_section_size;
dev_info(&device->device, "Send section size: %d, Section count:%d\n",
net_device->send_section_size, net_device->send_section_cnt);
@@ -412,8 +417,8 @@ static int netvsc_init_buf(struct hv_device *device)
net_device->map_words = DIV_ROUND_UP(net_device->send_section_cnt,
BITS_PER_LONG);
- net_device->send_section_map =
- kzalloc(net_device->map_words * sizeof(ulong), GFP_KERNEL);
+ net_device->send_section_map = kcalloc(net_device->map_words,
+ sizeof(ulong), GFP_KERNEL);
if (net_device->send_section_map == NULL) {
ret = -ENOMEM;
goto cleanup;
@@ -428,7 +433,6 @@ exit:
return ret;
}
-
/* Negotiate NVSP protocol version */
static int negotiate_nvsp_ver(struct hv_device *device,
struct netvsc_device *net_device,
@@ -468,9 +472,13 @@ static int negotiate_nvsp_ver(struct hv_device *device,
init_packet->msg.v2_msg.send_ndis_config.mtu = ndev->mtu + ETH_HLEN;
init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1;
- if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5)
+ if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5) {
init_packet->msg.v2_msg.send_ndis_config.capability.sriov = 1;
+ /* Teaming bit is needed to receive link speed updates */
+ init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1;
+ }
+
ret = vmbus_sendpacket(device->channel, init_packet,
sizeof(struct nvsp_message),
(unsigned long)init_packet,
@@ -485,9 +493,10 @@ static int netvsc_connect_vsp(struct hv_device *device)
struct netvsc_device *net_device;
struct nvsp_message *init_packet;
int ndis_version;
- u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2,
+ const u32 ver_list[] = {
+ NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2,
NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 };
- int i, num_ver = 4; /* number of different NVSP versions */
+ int i;
net_device = get_outbound_net_device(device);
if (!net_device)
@@ -496,7 +505,7 @@ static int netvsc_connect_vsp(struct hv_device *device)
init_packet = &net_device->channel_init_pkt;
/* Negotiate the latest NVSP protocol supported */
- for (i = num_ver - 1; i >= 0; i--)
+ for (i = ARRAY_SIZE(ver_list) - 1; i >= 0; i--)
if (negotiate_nvsp_ver(device, net_device, init_packet,
ver_list[i]) == 0) {
net_device->nvsp_version = ver_list[i];
@@ -555,7 +564,7 @@ static void netvsc_disconnect_vsp(struct hv_device *device)
/*
* netvsc_device_remove - Callback when the root bus device is removed
*/
-int netvsc_device_remove(struct hv_device *device)
+void netvsc_device_remove(struct hv_device *device)
{
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
@@ -577,10 +586,8 @@ int netvsc_device_remove(struct hv_device *device)
/* Release all resources */
vfree(net_device->sub_cb_buf);
free_netvsc_device(net_device);
- return 0;
}
-
#define RING_AVAIL_PERCENT_HIWATER 20
#define RING_AVAIL_PERCENT_LOWATER 10
@@ -604,72 +611,79 @@ static inline void netvsc_free_send_slot(struct netvsc_device *net_device,
sync_change_bit(index, net_device->send_section_map);
}
+static void netvsc_send_tx_complete(struct netvsc_device *net_device,
+ struct vmbus_channel *incoming_channel,
+ struct hv_device *device,
+ struct vmpacket_descriptor *packet)
+{
+ struct sk_buff *skb = (struct sk_buff *)(unsigned long)packet->trans_id;
+ struct net_device *ndev = hv_get_drvdata(device);
+ struct net_device_context *net_device_ctx = netdev_priv(ndev);
+ struct vmbus_channel *channel = device->channel;
+ int num_outstanding_sends;
+ u16 q_idx = 0;
+ int queue_sends;
+
+ /* Notify the layer above us */
+ if (likely(skb)) {
+ struct hv_netvsc_packet *nvsc_packet
+ = (struct hv_netvsc_packet *)skb->cb;
+ u32 send_index = nvsc_packet->send_buf_index;
+
+ if (send_index != NETVSC_INVALID_INDEX)
+ netvsc_free_send_slot(net_device, send_index);
+ q_idx = nvsc_packet->q_idx;
+ channel = incoming_channel;
+
+ dev_kfree_skb_any(skb);
+ }
+
+ num_outstanding_sends =
+ atomic_dec_return(&net_device->num_outstanding_sends);
+ queue_sends = atomic_dec_return(&net_device->queue_sends[q_idx]);
+
+ if (net_device->destroy && num_outstanding_sends == 0)
+ wake_up(&net_device->wait_drain);
+
+ if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
+ !net_device_ctx->start_remove &&
+ (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER ||
+ queue_sends < 1))
+ netif_tx_wake_queue(netdev_get_tx_queue(ndev, q_idx));
+}
+
static void netvsc_send_completion(struct netvsc_device *net_device,
struct vmbus_channel *incoming_channel,
struct hv_device *device,
struct vmpacket_descriptor *packet)
{
struct nvsp_message *nvsp_packet;
- struct hv_netvsc_packet *nvsc_packet;
struct net_device *ndev = hv_get_drvdata(device);
- struct net_device_context *net_device_ctx = netdev_priv(ndev);
- u32 send_index;
- struct sk_buff *skb;
nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
- (packet->offset8 << 3));
+ (packet->offset8 << 3));
- if ((nvsp_packet->hdr.msg_type == NVSP_MSG_TYPE_INIT_COMPLETE) ||
- (nvsp_packet->hdr.msg_type ==
- NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) ||
- (nvsp_packet->hdr.msg_type ==
- NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE) ||
- (nvsp_packet->hdr.msg_type ==
- NVSP_MSG5_TYPE_SUBCHANNEL)) {
+ switch (nvsp_packet->hdr.msg_type) {
+ case NVSP_MSG_TYPE_INIT_COMPLETE:
+ case NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE:
+ case NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE:
+ case NVSP_MSG5_TYPE_SUBCHANNEL:
/* Copy the response back */
memcpy(&net_device->channel_init_pkt, nvsp_packet,
sizeof(struct nvsp_message));
complete(&net_device->channel_init_wait);
- } else if (nvsp_packet->hdr.msg_type ==
- NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) {
- int num_outstanding_sends;
- u16 q_idx = 0;
- struct vmbus_channel *channel = device->channel;
- int queue_sends;
-
- /* Get the send context */
- skb = (struct sk_buff *)(unsigned long)packet->trans_id;
-
- /* Notify the layer above us */
- if (skb) {
- nvsc_packet = (struct hv_netvsc_packet *) skb->cb;
- send_index = nvsc_packet->send_buf_index;
- if (send_index != NETVSC_INVALID_INDEX)
- netvsc_free_send_slot(net_device, send_index);
- q_idx = nvsc_packet->q_idx;
- channel = incoming_channel;
- dev_kfree_skb_any(skb);
- }
-
- num_outstanding_sends =
- atomic_dec_return(&net_device->num_outstanding_sends);
- queue_sends = atomic_dec_return(&net_device->
- queue_sends[q_idx]);
+ break;
- if (net_device->destroy && num_outstanding_sends == 0)
- wake_up(&net_device->wait_drain);
+ case NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE:
+ netvsc_send_tx_complete(net_device, incoming_channel,
+ device, packet);
+ break;
- if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
- !net_device_ctx->start_remove &&
- (hv_ringbuf_avail_percent(&channel->outbound) >
- RING_AVAIL_PERCENT_HIWATER || queue_sends < 1))
- netif_tx_wake_queue(netdev_get_tx_queue(
- ndev, q_idx));
- } else {
- netdev_err(ndev, "Unknown send completion packet type- "
- "%d received!!\n", nvsp_packet->hdr.msg_type);
+ default:
+ netdev_err(ndev,
+ "Unknown send completion type %d received!!\n",
+ nvsp_packet->hdr.msg_type);
}
-
}
static u32 netvsc_get_next_send_section(struct netvsc_device *net_device)
@@ -859,7 +873,7 @@ int netvsc_send(struct hv_device *device,
struct sk_buff *skb)
{
struct netvsc_device *net_device;
- int ret = 0, m_ret = 0;
+ int ret = 0;
struct vmbus_channel *out_channel;
u16 q_idx = packet->q_idx;
u32 pktlen = packet->total_data_buflen, msd_len = 0;
@@ -948,8 +962,8 @@ int netvsc_send(struct hv_device *device,
}
if (msd_send) {
- m_ret = netvsc_send_pkt(device, msd_send, net_device,
- NULL, msd_skb);
+ int m_ret = netvsc_send_pkt(device, msd_send, net_device,
+ NULL, msd_skb);
if (m_ret != 0) {
netvsc_free_send_slot(net_device,
@@ -968,49 +982,121 @@ send_now:
return ret;
}
-static void netvsc_send_recv_completion(struct hv_device *device,
- struct vmbus_channel *channel,
- struct netvsc_device *net_device,
- u64 transaction_id, u32 status)
+static int netvsc_send_recv_completion(struct vmbus_channel *channel,
+ u64 transaction_id, u32 status)
{
struct nvsp_message recvcompMessage;
- int retries = 0;
int ret;
- struct net_device *ndev = hv_get_drvdata(device);
recvcompMessage.hdr.msg_type =
NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE;
recvcompMessage.msg.v1_msg.send_rndis_pkt_complete.status = status;
-retry_send_cmplt:
/* Send the completion */
ret = vmbus_sendpacket(channel, &recvcompMessage,
- sizeof(struct nvsp_message), transaction_id,
- VM_PKT_COMP, 0);
- if (ret == 0) {
- /* success */
- /* no-op */
- } else if (ret == -EAGAIN) {
- /* no more room...wait a bit and attempt to retry 3 times */
- 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 {
- netdev_err(ndev, "unable to send receive "
- "completion pkt (tid %llx)...give up retrying\n",
- transaction_id);
- }
- } else {
- netdev_err(ndev, "unable to send receive "
- "completion pkt - %llx\n", transaction_id);
+ sizeof(struct nvsp_message_header) + sizeof(u32),
+ transaction_id, VM_PKT_COMP, 0);
+
+ return ret;
+}
+
+static inline void count_recv_comp_slot(struct netvsc_device *nvdev, u16 q_idx,
+ u32 *filled, u32 *avail)
+{
+ u32 first = nvdev->mrc[q_idx].first;
+ u32 next = nvdev->mrc[q_idx].next;
+
+ *filled = (first > next) ? NETVSC_RECVSLOT_MAX - first + next :
+ next - first;
+
+ *avail = NETVSC_RECVSLOT_MAX - *filled - 1;
+}
+
+/* Read the first filled slot, no change to index */
+static inline struct recv_comp_data *read_recv_comp_slot(struct netvsc_device
+ *nvdev, u16 q_idx)
+{
+ u32 filled, avail;
+
+ if (!nvdev->mrc[q_idx].buf)
+ return NULL;
+
+ count_recv_comp_slot(nvdev, q_idx, &filled, &avail);
+ if (!filled)
+ return NULL;
+
+ return nvdev->mrc[q_idx].buf + nvdev->mrc[q_idx].first *
+ sizeof(struct recv_comp_data);
+}
+
+/* Put the first filled slot back to available pool */
+static inline void put_recv_comp_slot(struct netvsc_device *nvdev, u16 q_idx)
+{
+ int num_recv;
+
+ nvdev->mrc[q_idx].first = (nvdev->mrc[q_idx].first + 1) %
+ NETVSC_RECVSLOT_MAX;
+
+ num_recv = atomic_dec_return(&nvdev->num_outstanding_recvs);
+
+ if (nvdev->destroy && num_recv == 0)
+ wake_up(&nvdev->wait_drain);
+}
+
+/* Check and send pending recv completions */
+static void netvsc_chk_recv_comp(struct netvsc_device *nvdev,
+ struct vmbus_channel *channel, u16 q_idx)
+{
+ struct recv_comp_data *rcd;
+ int ret;
+
+ while (true) {
+ rcd = read_recv_comp_slot(nvdev, q_idx);
+ if (!rcd)
+ break;
+
+ ret = netvsc_send_recv_completion(channel, rcd->tid,
+ rcd->status);
+ if (ret)
+ break;
+
+ put_recv_comp_slot(nvdev, q_idx);
}
}
+#define NETVSC_RCD_WATERMARK 80
+
+/* Get next available slot */
+static inline struct recv_comp_data *get_recv_comp_slot(
+ struct netvsc_device *nvdev, struct vmbus_channel *channel, u16 q_idx)
+{
+ u32 filled, avail, next;
+ struct recv_comp_data *rcd;
+
+ if (!nvdev->recv_section)
+ return NULL;
+
+ if (!nvdev->mrc[q_idx].buf)
+ return NULL;
+
+ if (atomic_read(&nvdev->num_outstanding_recvs) >
+ nvdev->recv_section->num_sub_allocs * NETVSC_RCD_WATERMARK / 100)
+ netvsc_chk_recv_comp(nvdev, channel, q_idx);
+
+ count_recv_comp_slot(nvdev, q_idx, &filled, &avail);
+ if (!avail)
+ return NULL;
+
+ next = nvdev->mrc[q_idx].next;
+ rcd = nvdev->mrc[q_idx].buf + next * sizeof(struct recv_comp_data);
+ nvdev->mrc[q_idx].next = (next + 1) % NETVSC_RECVSLOT_MAX;
+
+ atomic_inc(&nvdev->num_outstanding_recvs);
+
+ return rcd;
+}
+
static void netvsc_receive(struct netvsc_device *net_device,
struct vmbus_channel *channel,
struct hv_device *device,
@@ -1025,6 +1111,9 @@ static void netvsc_receive(struct netvsc_device *net_device,
int count = 0;
struct net_device *ndev = hv_get_drvdata(device);
void *data;
+ int ret;
+ struct recv_comp_data *rcd;
+ u16 q_idx = channel->offermsg.offer.sub_channel_index;
/*
* All inbound packets other than send completion should be xfer page
@@ -1069,13 +1158,29 @@ static void netvsc_receive(struct netvsc_device *net_device,
/* Pass it to the upper layer */
status = rndis_filter_receive(device, netvsc_packet, &data,
channel);
+ }
+ if (!net_device->mrc[q_idx].buf) {
+ ret = netvsc_send_recv_completion(channel,
+ vmxferpage_packet->d.trans_id,
+ status);
+ if (ret)
+ netdev_err(ndev, "Recv_comp q:%hd, tid:%llx, err:%d\n",
+ q_idx, vmxferpage_packet->d.trans_id, ret);
+ return;
}
- netvsc_send_recv_completion(device, channel, net_device,
- vmxferpage_packet->d.trans_id, status);
-}
+ rcd = get_recv_comp_slot(net_device, channel, q_idx);
+ if (!rcd) {
+ netdev_err(ndev, "Recv_comp full buf q:%hd, tid:%llx\n",
+ q_idx, vmxferpage_packet->d.trans_id);
+ return;
+ }
+
+ rcd->tid = vmxferpage_packet->d.trans_id;
+ rcd->status = status;
+}
static void netvsc_send_table(struct hv_device *hdev,
struct nvsp_message *nvmsg)
@@ -1157,11 +1262,11 @@ static void netvsc_process_raw_pkt(struct hv_device *device,
}
}
-
void netvsc_channel_cb(void *context)
{
int ret;
struct vmbus_channel *channel = (struct vmbus_channel *)context;
+ u16 q_idx = channel->offermsg.offer.sub_channel_index;
struct hv_device *device;
struct netvsc_device *net_device;
u32 bytes_recvd;
@@ -1213,8 +1318,6 @@ void netvsc_channel_cb(void *context)
ndev,
request_id,
desc);
-
-
} else {
/*
* We are done for this pass.
@@ -1241,7 +1344,8 @@ void netvsc_channel_cb(void *context)
if (bufferlen > NETVSC_PACKET_SIZE)
kfree(buffer);
- return;
+
+ netvsc_chk_recv_comp(net_device, channel, q_idx);
}
/*
@@ -1263,9 +1367,6 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
net_device->ring_size = ring_size;
- /* Initialize the NetVSC channel extension */
- init_completion(&net_device->channel_init_wait);
-
set_per_channel_state(device->channel, net_device->cb_buffer);
/* Open the channel */