net/mlx5_core: Fix call to mlx5_core_qp_modify
[linux-2.6-block.git] / drivers / net / hyperv / netvsc.c
index 208eb05446baa4a6980620773865e3746a13848f..f69923695b5b1b6fbb886f523203ba86656ac5f6 100644 (file)
@@ -37,6 +37,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
 {
        struct netvsc_device *net_device;
        struct net_device *ndev = hv_get_drvdata(device);
+       int i;
 
        net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
        if (!net_device)
@@ -53,6 +54,11 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
        net_device->destroy = false;
        net_device->dev = device;
        net_device->ndev = ndev;
+       net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
+       net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
+
+       for (i = 0; i < num_online_cpus(); i++)
+               spin_lock_init(&net_device->msd[i].lock);
 
        hv_set_drvdata(device, net_device);
        return net_device;
@@ -687,12 +693,23 @@ static u32 netvsc_get_next_send_section(struct netvsc_device *net_device)
 
 static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
                                   unsigned int section_index,
+                                  u32 pend_size,
                                   struct hv_netvsc_packet *packet)
 {
        char *start = net_device->send_buf;
-       char *dest = (start + (section_index * net_device->send_section_size));
+       char *dest = start + (section_index * net_device->send_section_size)
+                    + pend_size;
        int i;
        u32 msg_size = 0;
+       u32 padding = 0;
+       u32 remain = packet->total_data_buflen % net_device->pkt_align;
+
+       /* Add padding */
+       if (packet->is_data_pkt && packet->xmit_more && remain) {
+               padding = net_device->pkt_align - remain;
+               packet->rndis_msg->msg_len += padding;
+               packet->total_data_buflen += padding;
+       }
 
        for (i = 0; i < packet->page_buf_cnt; i++) {
                char *src = phys_to_virt(packet->page_buf[i].pfn << PAGE_SHIFT);
@@ -703,67 +720,48 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
                msg_size += len;
                dest += len;
        }
+
+       if (padding) {
+               memset(dest, 0, padding);
+               msg_size += padding;
+       }
+
        return msg_size;
 }
 
-int netvsc_send(struct hv_device *device,
-                       struct hv_netvsc_packet *packet)
+static inline int netvsc_send_pkt(
+       struct hv_netvsc_packet *packet,
+       struct netvsc_device *net_device)
 {
-       struct netvsc_device *net_device;
-       int ret = 0;
-       struct nvsp_message sendMessage;
-       struct net_device *ndev;
-       struct vmbus_channel *out_channel = NULL;
-       u64 req_id;
-       unsigned int section_index = NETVSC_INVALID_INDEX;
-       u32 msg_size = 0;
-       struct sk_buff *skb = NULL;
+       struct nvsp_message nvmsg;
+       struct vmbus_channel *out_channel = packet->channel;
        u16 q_idx = packet->q_idx;
+       struct net_device *ndev = net_device->ndev;
+       u64 req_id;
+       int ret;
 
-
-       net_device = get_outbound_net_device(device);
-       if (!net_device)
-               return -ENODEV;
-       ndev = net_device->ndev;
-
-       sendMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
+       nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
        if (packet->is_data_pkt) {
                /* 0 is RMC_DATA; */
-               sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 0;
+               nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 0;
        } else {
                /* 1 is RMC_CONTROL; */
-               sendMessage.msg.v1_msg.send_rndis_pkt.channel_type = 1;
-       }
-
-       /* Attempt to send via sendbuf */
-       if (packet->total_data_buflen < net_device->send_section_size) {
-               section_index = netvsc_get_next_send_section(net_device);
-               if (section_index != NETVSC_INVALID_INDEX) {
-                       msg_size = netvsc_copy_to_send_buf(net_device,
-                                                          section_index,
-                                                          packet);
-                       skb = (struct sk_buff *)
-                             (unsigned long)packet->send_completion_tid;
-                       packet->page_buf_cnt = 0;
-               }
+               nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 1;
        }
-       packet->send_buf_index = section_index;
 
-
-       sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_index =
-               section_index;
-       sendMessage.msg.v1_msg.send_rndis_pkt.send_buf_section_size = msg_size;
+       nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_index =
+               packet->send_buf_index;
+       if (packet->send_buf_index == NETVSC_INVALID_INDEX)
+               nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size = 0;
+       else
+               nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size =
+                       packet->total_data_buflen;
 
        if (packet->send_completion)
                req_id = (ulong)packet;
        else
                req_id = 0;
 
-       out_channel = net_device->chn_table[packet->q_idx];
-       if (out_channel == NULL)
-               out_channel = device->channel;
-       packet->channel = out_channel;
-
        if (out_channel->rescind)
                return -ENODEV;
 
@@ -771,11 +769,12 @@ int netvsc_send(struct hv_device *device,
                ret = vmbus_sendpacket_pagebuffer(out_channel,
                                                  packet->page_buf,
                                                  packet->page_buf_cnt,
-                                                 &sendMessage,
+                                                 &nvmsg,
                                                  sizeof(struct nvsp_message),
                                                  req_id);
        } else {
-               ret = vmbus_sendpacket(out_channel, &sendMessage,
+               ret = vmbus_sendpacket(
+                               out_channel, &nvmsg,
                                sizeof(struct nvsp_message),
                                req_id,
                                VM_PKT_DATA_INBAND,
@@ -809,6 +808,107 @@ int netvsc_send(struct hv_device *device,
                           packet, ret);
        }
 
+       return ret;
+}
+
+int netvsc_send(struct hv_device *device,
+               struct hv_netvsc_packet *packet)
+{
+       struct netvsc_device *net_device;
+       int ret = 0, m_ret = 0;
+       struct vmbus_channel *out_channel;
+       u16 q_idx = packet->q_idx;
+       u32 pktlen = packet->total_data_buflen, msd_len = 0;
+       unsigned int section_index = NETVSC_INVALID_INDEX;
+       struct sk_buff *skb = NULL;
+       unsigned long flag;
+       struct multi_send_data *msdp;
+       struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL;
+
+       net_device = get_outbound_net_device(device);
+       if (!net_device)
+               return -ENODEV;
+
+       out_channel = net_device->chn_table[q_idx];
+       if (!out_channel) {
+               out_channel = device->channel;
+               q_idx = 0;
+               packet->q_idx = 0;
+       }
+       packet->channel = out_channel;
+       packet->send_buf_index = NETVSC_INVALID_INDEX;
+
+       msdp = &net_device->msd[q_idx];
+
+       /* batch packets in send buffer if possible */
+       spin_lock_irqsave(&msdp->lock, flag);
+       if (msdp->pkt)
+               msd_len = msdp->pkt->total_data_buflen;
+
+       if (packet->is_data_pkt && msd_len > 0 &&
+           msdp->count < net_device->max_pkt &&
+           msd_len + pktlen + net_device->pkt_align <
+           net_device->send_section_size) {
+               section_index = msdp->pkt->send_buf_index;
+
+       } else if (packet->is_data_pkt && pktlen + net_device->pkt_align <
+                  net_device->send_section_size) {
+               section_index = netvsc_get_next_send_section(net_device);
+               if (section_index != NETVSC_INVALID_INDEX) {
+                               msd_send = msdp->pkt;
+                               msdp->pkt = NULL;
+                               msdp->count = 0;
+                               msd_len = 0;
+               }
+       }
+
+       if (section_index != NETVSC_INVALID_INDEX) {
+               netvsc_copy_to_send_buf(net_device,
+                                       section_index, msd_len,
+                                       packet);
+               if (!packet->part_of_skb) {
+                       skb = (struct sk_buff *)
+                               (unsigned long)
+                               packet->send_completion_tid;
+
+                       packet->send_completion_tid = 0;
+               }
+
+               packet->page_buf_cnt = 0;
+               packet->send_buf_index = section_index;
+               packet->total_data_buflen += msd_len;
+
+               kfree(msdp->pkt);
+               if (packet->xmit_more) {
+                       msdp->pkt = packet;
+                       msdp->count++;
+               } else {
+                       cur_send = packet;
+                       msdp->pkt = NULL;
+                       msdp->count = 0;
+               }
+       } else {
+               msd_send = msdp->pkt;
+               msdp->pkt = NULL;
+               msdp->count = 0;
+               cur_send = packet;
+       }
+
+       spin_unlock_irqrestore(&msdp->lock, flag);
+
+       if (msd_send) {
+               m_ret = netvsc_send_pkt(msd_send, net_device);
+
+               if (m_ret != 0) {
+                       netvsc_free_send_slot(net_device,
+                                             msd_send->send_buf_index);
+                       kfree(msd_send);
+               }
+       }
+
+       if (cur_send)
+               ret = netvsc_send_pkt(cur_send, net_device);
+
        if (ret != 0) {
                if (section_index != NETVSC_INVALID_INDEX)
                        netvsc_free_send_slot(net_device, section_index);