Commit 4d3c46e6 authored by Vlad Yasevich's avatar Vlad Yasevich
Browse files

sctp: drop a_rwnd to 0 when receive buffer overflows.

SCTP has a problem that when small chunks are used, it is possible
to exhaust the receiver buffer without fully closing receive window.
This happens due to all overhead that we have account for with small
messages.  To fix this, when receive buffer is exceeded, we'll drop
the window to 0 and save the 'drop' portion.  When application starts
reading data and freeing up recevie buffer space, we'll wait until
we've reached the 'drop' window and then add back this 'drop' one
mtu at a time.  This worked well in testing and under stress produced
rather even recovery.
Signed-off-by: default avatarVlad Yasevich <>
parent 33ce8281
......@@ -1739,6 +1739,12 @@ struct sctp_association {
__u32 rwnd_over;
/* Keeps treack of rwnd pressure. This happens when we have
* a window, but not recevie buffer (i.e small packets). This one
* is releases slowly (1 PMTU at a time ).
__u32 rwnd_press;
/* This is the sndbuf size in use for the association.
* This corresponds to the sndbuf size for the association,
* as specified in the sk->sndbuf.
......@@ -202,6 +202,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->a_rwnd = asoc->rwnd;
asoc->rwnd_over = 0;
asoc->rwnd_press = 0;
/* Use my own max window until I learn something better. */
asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW;
......@@ -1374,6 +1375,17 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len)
asoc->rwnd += len;
/* If we had window pressure, start recovering it
* once our rwnd had reached the accumulated pressure
* threshold. The idea is to recover slowly, but up
* to the initial advertised window.
if (asoc->rwnd_press && asoc->rwnd >= asoc->rwnd_press) {
int change = min(asoc->pathmtu, asoc->rwnd_press);
asoc->rwnd += change;
asoc->rwnd_press -= change;
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) "
"- %u\n", __func__, asoc, len, asoc->rwnd,
asoc->rwnd_over, asoc->a_rwnd);
......@@ -1406,17 +1418,38 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned len)
/* Decrease asoc's rwnd by len. */
void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned len)
int rx_count;
int over = 0;
SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
if (asoc->ep->rcvbuf_policy)
rx_count = atomic_read(&asoc->rmem_alloc);
rx_count = atomic_read(&asoc->>sk_rmem_alloc);
/* If we've reached or overflowed our receive buffer, announce
* a 0 rwnd if rwnd would still be positive. Store the
* the pottential pressure overflow so that the window can be restored
* back to original value.
if (rx_count >= asoc->>sk_rcvbuf)
over = 1;
if (asoc->rwnd >= len) {
asoc->rwnd -= len;
if (over) {
asoc->rwnd_press = asoc->rwnd;
asoc->rwnd = 0;
} else {
asoc->rwnd_over = len - asoc->rwnd;
asoc->rwnd = 0;
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n",
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u, %u)\n",
__func__, asoc, len, asoc->rwnd,
asoc->rwnd_over, asoc->rwnd_press);
/* Build the bind address list for the association based on info from the
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment