#include "hyperv_vmbus.h"
 
 
-/* #defines */
-
-
-/* Amount of space to write to */
-#define BYTES_AVAIL_TO_WRITE(r, w, z) \
-       ((w) >= (r)) ? ((z) - ((w) - (r))) : ((r) - (w))
-
-
-/*
- *
- * hv_get_ringbuffer_availbytes()
- *
- * Get number of bytes available to read and to write to
- * for the specified ring buffer
- */
-static inline void
-hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
-                         u32 *read, u32 *write)
-{
-       u32 read_loc, write_loc;
-
-       smp_read_barrier_depends();
-
-       /* Capture the read/write indices before they changed */
-       read_loc = rbi->ring_buffer->read_index;
-       write_loc = rbi->ring_buffer->write_index;
-
-       *write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->ring_datasize);
-       *read = rbi->ring_datasize - *write;
-}
-
 /*
  * hv_get_next_write_location()
  *
 
        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)
 {
                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;
                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);
 
        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);
 
        u32 bytes_avail_towrite;
 };
 
+
+/*
+ *
+ * hv_get_ringbuffer_availbytes()
+ *
+ * Get number of bytes available to read and to write to
+ * for the specified ring buffer
+ */
+static inline void
+hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
+                         u32 *read, u32 *write)
+{
+       u32 read_loc, write_loc, dsize;
+
+       smp_read_barrier_depends();
+
+       /* Capture the read/write indices before they changed */
+       read_loc = rbi->ring_buffer->read_index;
+       write_loc = rbi->ring_buffer->write_index;
+       dsize = rbi->ring_datasize;
+
+       *write = write_loc >= read_loc ? dsize - (write_loc - read_loc) :
+               read_loc - write_loc;
+       *read = dsize - *write;
+}
+
+
 /*
  * We use the same version numbering for all Hyper-V modules.
  *