net/hyperv: Add flow control based on hi/low watermark
[sfrench/cifs-2.6.git] / drivers / net / hyperv / netvsc.c
index d025c83cd12a09fb4f701abce880f92aaed47b05..8b919471472fb1dba4d34ffcf0bfe5b4c723af7b 100644 (file)
@@ -428,6 +428,24 @@ int netvsc_device_remove(struct hv_device *device)
        return 0;
 }
 
+
+#define RING_AVAIL_PERCENT_HIWATER 20
+#define RING_AVAIL_PERCENT_LOWATER 10
+
+/*
+ * Get the percentage of available bytes to write in the ring.
+ * The return value is in range from 0 to 100.
+ */
+static inline u32 hv_ringbuf_avail_percent(
+               struct hv_ring_buffer_info *ring_info)
+{
+       u32 avail_read, avail_write;
+
+       hv_get_ringbuffer_availbytes(ring_info, &avail_read, &avail_write);
+
+       return avail_write * 100 / ring_info->ring_datasize;
+}
+
 static void netvsc_send_completion(struct hv_device *device,
                                   struct vmpacket_descriptor *packet)
 {
@@ -455,6 +473,8 @@ static void netvsc_send_completion(struct hv_device *device,
                complete(&net_device->channel_init_wait);
        } else if (nvsp_packet->hdr.msg_type ==
                   NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) {
+               int num_outstanding_sends;
+
                /* Get the send context */
                nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
                        packet->trans_id;
@@ -463,10 +483,14 @@ static void netvsc_send_completion(struct hv_device *device,
                nvsc_packet->completion.send.send_completion(
                        nvsc_packet->completion.send.send_completion_ctx);
 
-               atomic_dec(&net_device->num_outstanding_sends);
+               num_outstanding_sends =
+                       atomic_dec_return(&net_device->num_outstanding_sends);
 
-               if (netif_queue_stopped(ndev) && !net_device->start_remove)
-                       netif_wake_queue(ndev);
+               if (netif_queue_stopped(ndev) && !net_device->start_remove &&
+                       (hv_ringbuf_avail_percent(&device->channel->outbound)
+                       > RING_AVAIL_PERCENT_HIWATER ||
+                       num_outstanding_sends < 1))
+                               netif_wake_queue(ndev);
        } else {
                netdev_err(ndev, "Unknown send completion packet type- "
                           "%d received!!\n", nvsp_packet->hdr.msg_type);
@@ -519,10 +543,19 @@ int netvsc_send(struct hv_device *device,
 
        if (ret == 0) {
                atomic_inc(&net_device->num_outstanding_sends);
+               if (hv_ringbuf_avail_percent(&device->channel->outbound) <
+                       RING_AVAIL_PERCENT_LOWATER) {
+                       netif_stop_queue(ndev);
+                       if (atomic_read(&net_device->
+                               num_outstanding_sends) < 1)
+                               netif_wake_queue(ndev);
+               }
        } else if (ret == -EAGAIN) {
                netif_stop_queue(ndev);
-               if (atomic_read(&net_device->num_outstanding_sends) < 1)
+               if (atomic_read(&net_device->num_outstanding_sends) < 1) {
                        netif_wake_queue(ndev);
+                       ret = -ENOSPC;
+               }
        } else {
                netdev_err(ndev, "Unable to send packet %p ret %d\n",
                           packet, ret);