Blame SOURCES/0007-window_size-redid-window-handling-for-flow-control-r.patch

68eb44
From 8af6637d86b6a85e8889c286f7ff3d841fc5621c Mon Sep 17 00:00:00 2001
68eb44
From: Salvador Fandino <sfandino@yahoo.com>
68eb44
Date: Sat, 12 Oct 2013 02:51:46 +0200
68eb44
Subject: [PATCH 07/11] window_size: redid window handling for flow control reasons
68eb44
68eb44
Until now, the window size (channel->remote.window_size) was being
68eb44
updated just after receiving the packet from the transport layer.
68eb44
68eb44
That behaviour is wrong because the channel queue may grow uncontrolled
68eb44
when data arrives from the network faster that the upper layer consumes
68eb44
it.
68eb44
68eb44
This patch adds a new counter, read_avail, which keeps a count of the
68eb44
bytes available from the packet queue for reading. Also, now the window
68eb44
size is adjusted when the data is actually read by an upper layer.
68eb44
68eb44
That way, if the upper layer stops reading data, the window will
68eb44
eventually fill and the remote host will stop sending data. When the
68eb44
upper layers reads enough data, a window adjust packet is delivered and
68eb44
the transfer resumes.
68eb44
68eb44
The read_avail counter is used to detect the situation when the remote
68eb44
server tries to send data surpassing the window size. In that case, the
68eb44
extra data is discarded.
68eb44
68eb44
Signed-off-by: Salvador <sfandino@yahoo.com>
68eb44
68eb44
[upstream commit cdeef54967ed5b7d5bd8fa6da5851aa3d173faa0]
68eb44
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
68eb44
---
68eb44
 src/channel.c      |    8 +++++++-
68eb44
 src/libssh2_priv.h |    2 ++
68eb44
 src/packet.c       |   35 ++++++++++++++++++++++++++++-------
68eb44
 3 files changed, 37 insertions(+), 8 deletions(-)
68eb44
68eb44
diff --git a/src/channel.c b/src/channel.c
68eb44
index 74262d8..499d815 100644
68eb44
--- a/src/channel.c
68eb44
+++ b/src/channel.c
68eb44
@@ -1411,6 +1411,9 @@ _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid)
68eb44
         channel->flush_state = libssh2_NB_state_created;
68eb44
     }
68eb44
 
68eb44
+    channel->read_avail -= channel->flush_flush_bytes;
68eb44
+    channel->remote.window_size -= channel->flush_flush_bytes;
68eb44
+
68eb44
     if (channel->flush_refund_bytes) {
68eb44
         int rc;
68eb44
 
68eb44
@@ -1868,11 +1871,14 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
68eb44
         /* if the transport layer said EAGAIN then we say so as well */
68eb44
         return _libssh2_error(session, rc, "would block");
68eb44
     }
68eb44
-    else
68eb44
+    else {
68eb44
+        channel->read_avail -= bytes_read;
68eb44
+        channel->remote.window_size -= bytes_read;
68eb44
         /* make sure we remain in the created state to focus on emptying the
68eb44
            data we already have in the packet brigade before we try to read
68eb44
            more off the network again */
68eb44
         channel->read_state = libssh2_NB_state_created;
68eb44
+    }
68eb44
 
68eb44
     if(channel->remote.window_size < (LIBSSH2_CHANNEL_WINDOW_DEFAULT*30)) {
68eb44
         /* the window is getting too narrow, expand it! */
68eb44
diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
68eb44
index 4ec9f73..fcf4370 100644
68eb44
--- a/src/libssh2_priv.h
68eb44
+++ b/src/libssh2_priv.h
68eb44
@@ -357,6 +357,8 @@ struct _LIBSSH2_CHANNEL
68eb44
     libssh2_channel_data local, remote;
68eb44
     /* Amount of bytes to be refunded to receive window (but not yet sent) */
68eb44
     uint32_t adjust_queue;
68eb44
+    /* Data immediately available for reading */
68eb44
+    uint32_t read_avail;
68eb44
 
68eb44
     LIBSSH2_SESSION *session;
68eb44
 
68eb44
diff --git a/src/packet.c b/src/packet.c
68eb44
index bfbd56a..d2e758c 100644
68eb44
--- a/src/packet.c
68eb44
+++ b/src/packet.c
68eb44
@@ -653,6 +653,18 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
68eb44
                 _libssh2_debug(session, LIBSSH2_TRACE_CONN,
68eb44
                                "Ignoring extended data and refunding %d bytes",
68eb44
                                (int) (datalen - 13));
68eb44
+                if (channelp->read_avail + datalen - data_head >=
68eb44
+                    channelp->remote.window_size)
68eb44
+                    datalen = channelp->remote.window_size -
68eb44
+                        channelp->read_avail + data_head;
68eb44
+
68eb44
+                channelp->remote.window_size -= datalen - data_head;
68eb44
+                _libssh2_debug(session, LIBSSH2_TRACE_CONN,
68eb44
+                               "shrinking window size by %lu bytes to %lu, read_avail %lu",
68eb44
+                               datalen - data_head,
68eb44
+                               channelp->remote.window_size,
68eb44
+                               channelp->read_avail);
68eb44
+
68eb44
                 session->packAdd_channelp = channelp;
68eb44
 
68eb44
                 /* Adjust the window based on the block we just freed */
68eb44
@@ -684,7 +696,7 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
68eb44
                                " to receive, truncating");
68eb44
                 datalen = channelp->remote.packet_size + data_head;
68eb44
             }
68eb44
-            if (channelp->remote.window_size <= 0) {
68eb44
+            if (channelp->remote.window_size <= channelp->read_avail) {
68eb44
                 /*
68eb44
                  * Spec says we MAY ignore bytes sent beyond
68eb44
                  * window_size
68eb44
@@ -700,17 +712,26 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
68eb44
             /* Reset EOF status */
68eb44
             channelp->remote.eof = 0;
68eb44
 
68eb44
-            if ((datalen - data_head) > channelp->remote.window_size) {
68eb44
+            if (channelp->read_avail + datalen - data_head >
68eb44
+                channelp->remote.window_size) {
68eb44
                 _libssh2_error(session,
68eb44
                                LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
68eb44
                                "Remote sent more data than current "
68eb44
                                "window allows, truncating");
68eb44
-                datalen = channelp->remote.window_size + data_head;
68eb44
-                channelp->remote.window_size = 0;
68eb44
+                datalen = channelp->remote.window_size -
68eb44
+                    channelp->read_avail + data_head;
68eb44
             }
68eb44
-            else
68eb44
-                /* Now that we've received it, shrink our window */
68eb44
-                channelp->remote.window_size -= datalen - data_head;
68eb44
+
68eb44
+            /* Update the read_avail counter. The window size will be
68eb44
+             * updated once the data is actually read from the queue
68eb44
+             * from an upper layer */
68eb44
+            channelp->read_avail += datalen - data_head;
68eb44
+
68eb44
+            _libssh2_debug(session, LIBSSH2_TRACE_CONN,
68eb44
+                           "increasing read_avail by %lu bytes to %lu/%lu",
68eb44
+                           (long)(datalen - data_head),
68eb44
+                           (long)channelp->read_avail,
68eb44
+                           (long)channelp->remote.window_size);
68eb44
 
68eb44
             break;
68eb44
 
68eb44
-- 
68eb44
1.7.1
68eb44