diff options
Diffstat (limited to 'drivers/net/hyperv/netvsc.c')
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 103 |
1 files changed, 72 insertions, 31 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0265d703eb03..c9910c33e671 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -36,6 +36,7 @@ #include <asm/sync_bitops.h> #include "hyperv_net.h" +#include "netvsc_trace.h" /* * Switch the data path from the synthetic interface to the VF @@ -57,6 +58,8 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) init_pkt->msg.v4_msg.active_dp.active_datapath = NVSP_DATAPATH_SYNTHETIC; + trace_nvsp_send(ndev, init_pkt); + vmbus_sendpacket(dev->channel, init_pkt, sizeof(struct nvsp_message), (unsigned long)init_pkt, @@ -90,6 +93,11 @@ static void free_netvsc_device(struct rcu_head *head) = container_of(head, struct netvsc_device, rcu); int i; + kfree(nvdev->extension); + vfree(nvdev->recv_buf); + vfree(nvdev->send_buf); + kfree(nvdev->send_section_map); + for (i = 0; i < VRSS_CHANNEL_MAX; i++) vfree(nvdev->chan_table[i].mrc.slots); @@ -124,6 +132,8 @@ static void netvsc_revoke_buf(struct hv_device *device, revoke_packet->msg.v1_msg. revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; + trace_nvsp_send(ndev, revoke_packet); + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), @@ -164,6 +174,8 @@ static void netvsc_revoke_buf(struct hv_device *device, revoke_packet->msg.v1_msg.revoke_send_buf.id = NETVSC_SEND_BUFFER_ID; + trace_nvsp_send(ndev, revoke_packet); + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), @@ -211,12 +223,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device, net_device->recv_buf_gpadl_handle = 0; } - if (net_device->recv_buf) { - /* Free up the receive buffer */ - vfree(net_device->recv_buf); - net_device->recv_buf = NULL; - } - if (net_device->send_buf_gpadl_handle) { ret = vmbus_teardown_gpadl(device->channel, net_device->send_buf_gpadl_handle); @@ -231,12 +237,6 @@ static void netvsc_teardown_gpadl(struct hv_device *device, } net_device->send_buf_gpadl_handle = 0; } - if (net_device->send_buf) { - /* Free up the send buffer */ - vfree(net_device->send_buf); - net_device->send_buf = NULL; - } - kfree(net_device->send_section_map); } int netvsc_alloc_recv_comp_ring(struct netvsc_device *net_device, u32 q_idx) @@ -282,6 +282,8 @@ static int netvsc_init_buf(struct hv_device *device, goto cleanup; } + net_device->recv_buf_size = buf_size; + /* * Establish the gpadl handle for this buffer on this * channel. Note: This call uses the vmbus connection rather @@ -305,6 +307,8 @@ static int netvsc_init_buf(struct hv_device *device, init_packet->msg.v1_msg. send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; + trace_nvsp_send(ndev, init_packet); + /* Send the gpadl notification request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -384,6 +388,8 @@ static int netvsc_init_buf(struct hv_device *device, net_device->send_buf_gpadl_handle; init_packet->msg.v1_msg.send_send_buf.id = NETVSC_SEND_BUFFER_ID; + trace_nvsp_send(ndev, init_packet); + /* Send the gpadl notification request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -452,6 +458,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, init_packet->msg.init_msg.init.min_protocol_ver = nvsp_ver; init_packet->msg.init_msg.init.max_protocol_ver = nvsp_ver; + trace_nvsp_send(ndev, init_packet); + /* Send the init request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -484,6 +492,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1; } + trace_nvsp_send(ndev, init_packet); + ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), (unsigned long)init_packet, @@ -496,6 +506,7 @@ static int netvsc_connect_vsp(struct hv_device *device, struct netvsc_device *net_device, const struct netvsc_device_info *device_info) { + struct net_device *ndev = hv_get_drvdata(device); static const u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2, NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 @@ -536,6 +547,8 @@ static int netvsc_connect_vsp(struct hv_device *device, send_ndis_ver.ndis_minor_ver = ndis_version & 0xFFFF; + trace_nvsp_send(ndev, init_packet); + /* Send the init request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -562,26 +575,29 @@ void netvsc_device_remove(struct hv_device *device) = rtnl_dereference(net_device_ctx->nvdev); int i; - cancel_work_sync(&net_device->subchan_work); - netvsc_revoke_buf(device, net_device); RCU_INIT_POINTER(net_device_ctx->nvdev, NULL); + /* And disassociate NAPI context from device */ + for (i = 0; i < net_device->num_chn; i++) + netif_napi_del(&net_device->chan_table[i].napi); + /* * At this point, no one should be accessing net_device * except in here */ netdev_dbg(ndev, "net device safe to remove\n"); + /* older versions require that buffer be revoked before close */ + if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_4) + netvsc_teardown_gpadl(device, net_device); + /* Now, we can close the channel safely */ vmbus_close(device->channel); - netvsc_teardown_gpadl(device, net_device); - - /* And dissassociate NAPI context from device */ - for (i = 0; i < net_device->num_chn; i++) - netif_napi_del(&net_device->chan_table[i].napi); + if (net_device->nvsp_version >= NVSP_PROTOCOL_VERSION_4) + netvsc_teardown_gpadl(device, net_device); /* Release all resources */ free_netvsc_device_rcu(net_device); @@ -645,14 +661,18 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, queue_sends = atomic_dec_return(&net_device->chan_table[q_idx].queue_sends); - if (net_device->destroy && queue_sends == 0) - wake_up(&net_device->wait_drain); + if (unlikely(net_device->destroy)) { + if (queue_sends == 0) + wake_up(&net_device->wait_drain); + } else { + struct netdev_queue *txq = netdev_get_tx_queue(ndev, q_idx); - if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) && - (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER || - queue_sends < 1)) { - netif_tx_wake_queue(netdev_get_tx_queue(ndev, q_idx)); - ndev_ctx->eth_stats.wake_queue++; + if (netif_tx_queue_stopped(txq) && + (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER || + queue_sends < 1)) { + netif_tx_wake_queue(txq); + ndev_ctx->eth_stats.wake_queue++; + } } } @@ -747,7 +767,7 @@ static inline int netvsc_send_pkt( struct sk_buff *skb) { struct nvsp_message nvmsg; - struct nvsp_1_message_send_rndis_packet * const rpkt = + struct nvsp_1_message_send_rndis_packet *rpkt = &nvmsg.msg.v1_msg.send_rndis_pkt; struct netvsc_channel * const nvchan = &net_device->chan_table[packet->q_idx]; @@ -776,6 +796,8 @@ static inline int netvsc_send_pkt( if (out_channel->rescind) return -ENODEV; + trace_nvsp_send_pkt(ndev, out_channel, rpkt); + if (packet->page_buf_cnt) { if (packet->cp_partial) pb += packet->rmsg_pgcnt; @@ -1075,13 +1097,30 @@ static int netvsc_receive(struct net_device *ndev, /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < count; i++) { - void *data = recv_buf - + vmxferpage_packet->ranges[i].byte_offset; + u32 offset = vmxferpage_packet->ranges[i].byte_offset; u32 buflen = vmxferpage_packet->ranges[i].byte_count; + void *data; + int ret; + + if (unlikely(offset + buflen > net_device->recv_buf_size)) { + status = NVSP_STAT_FAIL; + netif_err(net_device_ctx, rx_err, ndev, + "Packet offset:%u + len:%u too big\n", + offset, buflen); + + continue; + } + + data = recv_buf + offset; + + trace_rndis_recv(ndev, q_idx, data); /* Pass it to the upper layer */ - status = rndis_filter_receive(ndev, net_device, - channel, data, buflen); + ret = rndis_filter_receive(ndev, net_device, + channel, data, buflen); + + if (unlikely(ret != NVSP_STAT_SUCCESS)) + status = NVSP_STAT_FAIL; } enq_receive_complete(ndev, net_device, q_idx, @@ -1143,6 +1182,8 @@ static int netvsc_process_raw_pkt(struct hv_device *device, struct net_device_context *net_device_ctx = netdev_priv(ndev); struct nvsp_message *nvmsg = hv_pkt_data(desc); + trace_nvsp_recv(ndev, channel, nvmsg); + switch (desc->type) { case VM_PKT_COMP: netvsc_send_completion(net_device, channel, device, |