diff options
-rw-r--r-- | include/net/sctp/structs.h | 14 | ||||
-rw-r--r-- | net/sctp/associola.c | 82 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 2 | ||||
-rw-r--r-- | net/sctp/socket.c | 6 | ||||
-rw-r--r-- | net/sctp/ulpevent.c | 8 |
5 files changed, 25 insertions, 87 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d992ca3145fe..6ee76c804893 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -1653,17 +1653,6 @@ struct sctp_association { | |||
1653 | /* This is the last advertised value of rwnd over a SACK chunk. */ | 1653 | /* This is the last advertised value of rwnd over a SACK chunk. */ |
1654 | __u32 a_rwnd; | 1654 | __u32 a_rwnd; |
1655 | 1655 | ||
1656 | /* Number of bytes by which the rwnd has slopped. The rwnd is allowed | ||
1657 | * to slop over a maximum of the association's frag_point. | ||
1658 | */ | ||
1659 | __u32 rwnd_over; | ||
1660 | |||
1661 | /* Keeps treack of rwnd pressure. This happens when we have | ||
1662 | * a window, but not recevie buffer (i.e small packets). This one | ||
1663 | * is releases slowly (1 PMTU at a time ). | ||
1664 | */ | ||
1665 | __u32 rwnd_press; | ||
1666 | |||
1667 | /* This is the sndbuf size in use for the association. | 1656 | /* This is the sndbuf size in use for the association. |
1668 | * This corresponds to the sndbuf size for the association, | 1657 | * This corresponds to the sndbuf size for the association, |
1669 | * as specified in the sk->sndbuf. | 1658 | * as specified in the sk->sndbuf. |
@@ -1892,8 +1881,7 @@ void sctp_assoc_update(struct sctp_association *old, | |||
1892 | __u32 sctp_association_get_next_tsn(struct sctp_association *); | 1881 | __u32 sctp_association_get_next_tsn(struct sctp_association *); |
1893 | 1882 | ||
1894 | void sctp_assoc_sync_pmtu(struct sock *, struct sctp_association *); | 1883 | void sctp_assoc_sync_pmtu(struct sock *, struct sctp_association *); |
1895 | void sctp_assoc_rwnd_increase(struct sctp_association *, unsigned int); | 1884 | void sctp_assoc_rwnd_update(struct sctp_association *, bool); |
1896 | void sctp_assoc_rwnd_decrease(struct sctp_association *, unsigned int); | ||
1897 | void sctp_assoc_set_primary(struct sctp_association *, | 1885 | void sctp_assoc_set_primary(struct sctp_association *, |
1898 | struct sctp_transport *); | 1886 | struct sctp_transport *); |
1899 | void sctp_assoc_del_nonprimary_peers(struct sctp_association *, | 1887 | void sctp_assoc_del_nonprimary_peers(struct sctp_association *, |
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 5ae609200674..f558433537b8 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c | |||
@@ -1367,44 +1367,35 @@ static inline bool sctp_peer_needs_update(struct sctp_association *asoc) | |||
1367 | return false; | 1367 | return false; |
1368 | } | 1368 | } |
1369 | 1369 | ||
1370 | /* Increase asoc's rwnd by len and send any window update SACK if needed. */ | 1370 | /* Update asoc's rwnd for the approximated state in the buffer, |
1371 | void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len) | 1371 | * and check whether SACK needs to be sent. |
1372 | */ | ||
1373 | void sctp_assoc_rwnd_update(struct sctp_association *asoc, bool update_peer) | ||
1372 | { | 1374 | { |
1375 | int rx_count; | ||
1373 | struct sctp_chunk *sack; | 1376 | struct sctp_chunk *sack; |
1374 | struct timer_list *timer; | 1377 | struct timer_list *timer; |
1375 | 1378 | ||
1376 | if (asoc->rwnd_over) { | 1379 | if (asoc->ep->rcvbuf_policy) |
1377 | if (asoc->rwnd_over >= len) { | 1380 | rx_count = atomic_read(&asoc->rmem_alloc); |
1378 | asoc->rwnd_over -= len; | 1381 | else |
1379 | } else { | 1382 | rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc); |
1380 | asoc->rwnd += (len - asoc->rwnd_over); | ||
1381 | asoc->rwnd_over = 0; | ||
1382 | } | ||
1383 | } else { | ||
1384 | asoc->rwnd += len; | ||
1385 | } | ||
1386 | 1383 | ||
1387 | /* If we had window pressure, start recovering it | 1384 | if ((asoc->base.sk->sk_rcvbuf - rx_count) > 0) |
1388 | * once our rwnd had reached the accumulated pressure | 1385 | asoc->rwnd = (asoc->base.sk->sk_rcvbuf - rx_count) >> 1; |
1389 | * threshold. The idea is to recover slowly, but up | 1386 | else |
1390 | * to the initial advertised window. | 1387 | asoc->rwnd = 0; |
1391 | */ | ||
1392 | if (asoc->rwnd_press && asoc->rwnd >= asoc->rwnd_press) { | ||
1393 | int change = min(asoc->pathmtu, asoc->rwnd_press); | ||
1394 | asoc->rwnd += change; | ||
1395 | asoc->rwnd_press -= change; | ||
1396 | } | ||
1397 | 1388 | ||
1398 | pr_debug("%s: asoc:%p rwnd increased by %d to (%u, %u) - %u\n", | 1389 | pr_debug("%s: asoc:%p rwnd=%u, rx_count=%d, sk_rcvbuf=%d\n", |
1399 | __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, | 1390 | __func__, asoc, asoc->rwnd, rx_count, |
1400 | asoc->a_rwnd); | 1391 | asoc->base.sk->sk_rcvbuf); |
1401 | 1392 | ||
1402 | /* Send a window update SACK if the rwnd has increased by at least the | 1393 | /* Send a window update SACK if the rwnd has increased by at least the |
1403 | * minimum of the association's PMTU and half of the receive buffer. | 1394 | * minimum of the association's PMTU and half of the receive buffer. |
1404 | * The algorithm used is similar to the one described in | 1395 | * The algorithm used is similar to the one described in |
1405 | * Section 4.2.3.3 of RFC 1122. | 1396 | * Section 4.2.3.3 of RFC 1122. |
1406 | */ | 1397 | */ |
1407 | if (sctp_peer_needs_update(asoc)) { | 1398 | if (update_peer && sctp_peer_needs_update(asoc)) { |
1408 | asoc->a_rwnd = asoc->rwnd; | 1399 | asoc->a_rwnd = asoc->rwnd; |
1409 | 1400 | ||
1410 | pr_debug("%s: sending window update SACK- asoc:%p rwnd:%u " | 1401 | pr_debug("%s: sending window update SACK- asoc:%p rwnd:%u " |
@@ -1426,45 +1417,6 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len) | |||
1426 | } | 1417 | } |
1427 | } | 1418 | } |
1428 | 1419 | ||
1429 | /* Decrease asoc's rwnd by len. */ | ||
1430 | void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len) | ||
1431 | { | ||
1432 | int rx_count; | ||
1433 | int over = 0; | ||
1434 | |||
1435 | if (unlikely(!asoc->rwnd || asoc->rwnd_over)) | ||
1436 | pr_debug("%s: association:%p has asoc->rwnd:%u, " | ||
1437 | "asoc->rwnd_over:%u!\n", __func__, asoc, | ||
1438 | asoc->rwnd, asoc->rwnd_over); | ||
1439 | |||
1440 | if (asoc->ep->rcvbuf_policy) | ||
1441 | rx_count = atomic_read(&asoc->rmem_alloc); | ||
1442 | else | ||
1443 | rx_count = atomic_read(&asoc->base.sk->sk_rmem_alloc); | ||
1444 | |||
1445 | /* If we've reached or overflowed our receive buffer, announce | ||
1446 | * a 0 rwnd if rwnd would still be positive. Store the | ||
1447 | * the potential pressure overflow so that the window can be restored | ||
1448 | * back to original value. | ||
1449 | */ | ||
1450 | if (rx_count >= asoc->base.sk->sk_rcvbuf) | ||
1451 | over = 1; | ||
1452 | |||
1453 | if (asoc->rwnd >= len) { | ||
1454 | asoc->rwnd -= len; | ||
1455 | if (over) { | ||
1456 | asoc->rwnd_press += asoc->rwnd; | ||
1457 | asoc->rwnd = 0; | ||
1458 | } | ||
1459 | } else { | ||
1460 | asoc->rwnd_over = len - asoc->rwnd; | ||
1461 | asoc->rwnd = 0; | ||
1462 | } | ||
1463 | |||
1464 | pr_debug("%s: asoc:%p rwnd decreased by %d to (%u, %u, %u)\n", | ||
1465 | __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, | ||
1466 | asoc->rwnd_press); | ||
1467 | } | ||
1468 | 1420 | ||
1469 | /* Build the bind address list for the association based on info from the | 1421 | /* Build the bind address list for the association based on info from the |
1470 | * local endpoint and the remote peer. | 1422 | * local endpoint and the remote peer. |
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 483dcd71b3c5..591b44d3b7de 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c | |||
@@ -6176,7 +6176,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, | |||
6176 | * PMTU. In cases, such as loopback, this might be a rather | 6176 | * PMTU. In cases, such as loopback, this might be a rather |
6177 | * large spill over. | 6177 | * large spill over. |
6178 | */ | 6178 | */ |
6179 | if ((!chunk->data_accepted) && (!asoc->rwnd || asoc->rwnd_over || | 6179 | if ((!chunk->data_accepted) && (!asoc->rwnd || |
6180 | (datalen > asoc->rwnd + asoc->frag_point))) { | 6180 | (datalen > asoc->rwnd + asoc->frag_point))) { |
6181 | 6181 | ||
6182 | /* If this is the next TSN, consider reneging to make | 6182 | /* If this is the next TSN, consider reneging to make |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 9e91d6e5df63..7075ac847fde 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -2092,12 +2092,6 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, | |||
2092 | sctp_skb_pull(skb, copied); | 2092 | sctp_skb_pull(skb, copied); |
2093 | skb_queue_head(&sk->sk_receive_queue, skb); | 2093 | skb_queue_head(&sk->sk_receive_queue, skb); |
2094 | 2094 | ||
2095 | /* When only partial message is copied to the user, increase | ||
2096 | * rwnd by that amount. If all the data in the skb is read, | ||
2097 | * rwnd is updated when the event is freed. | ||
2098 | */ | ||
2099 | if (!sctp_ulpevent_is_notification(event)) | ||
2100 | sctp_assoc_rwnd_increase(event->asoc, copied); | ||
2101 | goto out; | 2095 | goto out; |
2102 | } else if ((event->msg_flags & MSG_NOTIFICATION) || | 2096 | } else if ((event->msg_flags & MSG_NOTIFICATION) || |
2103 | (event->msg_flags & MSG_EOR)) | 2097 | (event->msg_flags & MSG_EOR)) |
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 85c64658bd0b..8d198ae03606 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c | |||
@@ -989,7 +989,7 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event, | |||
989 | skb = sctp_event2skb(event); | 989 | skb = sctp_event2skb(event); |
990 | /* Set the owner and charge rwnd for bytes received. */ | 990 | /* Set the owner and charge rwnd for bytes received. */ |
991 | sctp_ulpevent_set_owner(event, asoc); | 991 | sctp_ulpevent_set_owner(event, asoc); |
992 | sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb)); | 992 | sctp_assoc_rwnd_update(asoc, false); |
993 | 993 | ||
994 | if (!skb->data_len) | 994 | if (!skb->data_len) |
995 | return; | 995 | return; |
@@ -1011,6 +1011,7 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) | |||
1011 | { | 1011 | { |
1012 | struct sk_buff *skb, *frag; | 1012 | struct sk_buff *skb, *frag; |
1013 | unsigned int len; | 1013 | unsigned int len; |
1014 | struct sctp_association *asoc; | ||
1014 | 1015 | ||
1015 | /* Current stack structures assume that the rcv buffer is | 1016 | /* Current stack structures assume that the rcv buffer is |
1016 | * per socket. For UDP style sockets this is not true as | 1017 | * per socket. For UDP style sockets this is not true as |
@@ -1035,8 +1036,11 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) | |||
1035 | } | 1036 | } |
1036 | 1037 | ||
1037 | done: | 1038 | done: |
1038 | sctp_assoc_rwnd_increase(event->asoc, len); | 1039 | asoc = event->asoc; |
1040 | sctp_association_hold(asoc); | ||
1039 | sctp_ulpevent_release_owner(event); | 1041 | sctp_ulpevent_release_owner(event); |
1042 | sctp_assoc_rwnd_update(asoc, true); | ||
1043 | sctp_association_put(asoc); | ||
1040 | } | 1044 | } |
1041 | 1045 | ||
1042 | static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event) | 1046 | static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event) |