diff options
Diffstat (limited to 'drivers/net/hyperv/netvsc.c')
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 331 |
1 files changed, 216 insertions, 115 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 410fb8e81376..720b5fa9e625 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_consume_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; @@ -930,7 +944,7 @@ int netvsc_send(struct hv_device *device, } if (msdp->skb) - dev_kfree_skb_any(msdp->skb); + dev_consume_skb_any(msdp->skb); if (xmit_more && !packet->cp_partial) { msdp->skb = skb; @@ -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 */ |