aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2012-06-29 23:04:26 -0400
committerDavid S. Miller <davem@davemloft.net>2012-07-01 01:44:35 -0400
commit4244854d22bf8f782698c5224b9191c8d2d42610 (patch)
tree1c6d81517625a33e427a183587783a966960e135
parent0e90b49ca4b891f085b57559a3071a4feefb496c (diff)
sctp: be more restrictive in transport selection on bundled sacks
It was noticed recently that when we send data on a transport, its possible that we might bundle a sack that arrived on a different transport. While this isn't a major problem, it does go against the SHOULD requirement in section 6.4 of RFC 2960: An endpoint SHOULD transmit reply chunks (e.g., SACK, HEARTBEAT ACK, etc.) to the same destination transport address from which it received the DATA or control chunk to which it is replying. This rule should also be followed if the endpoint is bundling DATA chunks together with the reply chunk. This patch seeks to correct that. It restricts the bundling of sack operations to only those transports which have moved the ctsn of the association forward since the last sack. By doing this we guarantee that we only bundle outbound saks on a transport that has received a chunk since the last sack. This brings us into stricter compliance with the RFC. Vlad had initially suggested that we strictly allow only sack bundling on the transport that last moved the ctsn forward. While this makes sense, I was concerned that doing so prevented us from bundling in the case where we had received chunks that moved the ctsn on multiple transports. In those cases, the RFC allows us to select any of the transports having received chunks to bundle the sack on. so I've modified the approach to allow for that, by adding a state variable to each transport that tracks weather it has moved the ctsn since the last sack. This I think keeps our behavior (and performance), close enough to our current profile that I think we can do this without a sysctl knob to enable/disable it. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> CC: Vlad Yaseivch <vyasevich@gmail.com> CC: David S. Miller <davem@davemloft.net> CC: linux-sctp@vger.kernel.org Reported-by: Michele Baldessari <michele@redhat.com> Reported-by: sorin serban <sserban@redhat.com> Acked-by: Vlad Yasevich <vyasevich@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sctp/structs.h4
-rw-r--r--include/net/sctp/tsnmap.h3
-rw-r--r--net/sctp/associola.c1
-rw-r--r--net/sctp/output.c5
-rw-r--r--net/sctp/sm_make_chunk.c16
-rw-r--r--net/sctp/sm_sideeffect.c2
-rw-r--r--net/sctp/transport.c2
-rw-r--r--net/sctp/tsnmap.c6
-rw-r--r--net/sctp/ulpevent.c3
-rw-r--r--net/sctp/ulpqueue.c2
10 files changed, 39 insertions, 5 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index e4652fe58958..fecdf31816f2 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -912,6 +912,9 @@ struct sctp_transport {
912 /* Is this structure kfree()able? */ 912 /* Is this structure kfree()able? */
913 malloced:1; 913 malloced:1;
914 914
915 /* Has this transport moved the ctsn since we last sacked */
916 __u32 sack_generation;
917
915 struct flowi fl; 918 struct flowi fl;
916 919
917 /* This is the peer's IP address and port. */ 920 /* This is the peer's IP address and port. */
@@ -1584,6 +1587,7 @@ struct sctp_association {
1584 */ 1587 */
1585 __u8 sack_needed; /* Do we need to sack the peer? */ 1588 __u8 sack_needed; /* Do we need to sack the peer? */
1586 __u32 sack_cnt; 1589 __u32 sack_cnt;
1590 __u32 sack_generation;
1587 1591
1588 /* These are capabilities which our peer advertised. */ 1592 /* These are capabilities which our peer advertised. */
1589 __u8 ecn_capable:1, /* Can peer do ECN? */ 1593 __u8 ecn_capable:1, /* Can peer do ECN? */
diff --git a/include/net/sctp/tsnmap.h b/include/net/sctp/tsnmap.h
index e7728bc14ccf..2c5d2b4d5d1e 100644
--- a/include/net/sctp/tsnmap.h
+++ b/include/net/sctp/tsnmap.h
@@ -117,7 +117,8 @@ void sctp_tsnmap_free(struct sctp_tsnmap *map);
117int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn); 117int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn);
118 118
119/* Mark this TSN as seen. */ 119/* Mark this TSN as seen. */
120int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn); 120int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn,
121 struct sctp_transport *trans);
121 122
122/* Mark this TSN and all lower as seen. */ 123/* Mark this TSN and all lower as seen. */
123void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn); 124void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 5bc9ab161b37..b16517ee1aaf 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -271,6 +271,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
271 */ 271 */
272 asoc->peer.sack_needed = 1; 272 asoc->peer.sack_needed = 1;
273 asoc->peer.sack_cnt = 0; 273 asoc->peer.sack_cnt = 0;
274 asoc->peer.sack_generation = 1;
274 275
275 /* Assume that the peer will tell us if he recognizes ASCONF 276 /* Assume that the peer will tell us if he recognizes ASCONF
276 * as part of INIT exchange. 277 * as part of INIT exchange.
diff --git a/net/sctp/output.c b/net/sctp/output.c
index f1b7d4bb591e..6ae47acaaec6 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -248,6 +248,11 @@ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt,
248 /* If the SACK timer is running, we have a pending SACK */ 248 /* If the SACK timer is running, we have a pending SACK */
249 if (timer_pending(timer)) { 249 if (timer_pending(timer)) {
250 struct sctp_chunk *sack; 250 struct sctp_chunk *sack;
251
252 if (pkt->transport->sack_generation !=
253 pkt->transport->asoc->peer.sack_generation)
254 return retval;
255
251 asoc->a_rwnd = asoc->rwnd; 256 asoc->a_rwnd = asoc->rwnd;
252 sack = sctp_make_sack(asoc); 257 sack = sctp_make_sack(asoc);
253 if (sack) { 258 if (sack) {
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index a85eeeb55dd0..b6de71efb140 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -734,8 +734,10 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
734 int len; 734 int len;
735 __u32 ctsn; 735 __u32 ctsn;
736 __u16 num_gabs, num_dup_tsns; 736 __u16 num_gabs, num_dup_tsns;
737 struct sctp_association *aptr = (struct sctp_association *)asoc;
737 struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; 738 struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
738 struct sctp_gap_ack_block gabs[SCTP_MAX_GABS]; 739 struct sctp_gap_ack_block gabs[SCTP_MAX_GABS];
740 struct sctp_transport *trans;
739 741
740 memset(gabs, 0, sizeof(gabs)); 742 memset(gabs, 0, sizeof(gabs));
741 ctsn = sctp_tsnmap_get_ctsn(map); 743 ctsn = sctp_tsnmap_get_ctsn(map);
@@ -805,6 +807,20 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
805 sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns, 807 sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
806 sctp_tsnmap_get_dups(map)); 808 sctp_tsnmap_get_dups(map));
807 809
810 /* Once we have a sack generated, check to see what our sack
811 * generation is, if its 0, reset the transports to 0, and reset
812 * the association generation to 1
813 *
814 * The idea is that zero is never used as a valid generation for the
815 * association so no transport will match after a wrap event like this,
816 * Until the next sack
817 */
818 if (++aptr->peer.sack_generation == 0) {
819 list_for_each_entry(trans, &asoc->peer.transport_addr_list,
820 transports)
821 trans->sack_generation = 0;
822 aptr->peer.sack_generation = 1;
823 }
808nodata: 824nodata:
809 return retval; 825 return retval;
810} 826}
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index c96d1a81cf42..8716da1a8592 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1268,7 +1268,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
1268 case SCTP_CMD_REPORT_TSN: 1268 case SCTP_CMD_REPORT_TSN:
1269 /* Record the arrival of a TSN. */ 1269 /* Record the arrival of a TSN. */
1270 error = sctp_tsnmap_mark(&asoc->peer.tsn_map, 1270 error = sctp_tsnmap_mark(&asoc->peer.tsn_map,
1271 cmd->obj.u32); 1271 cmd->obj.u32, NULL);
1272 break; 1272 break;
1273 1273
1274 case SCTP_CMD_REPORT_FWDTSN: 1274 case SCTP_CMD_REPORT_FWDTSN:
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index b026ba0c6992..1dcceb6e0ce6 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -68,6 +68,8 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
68 peer->af_specific = sctp_get_af_specific(addr->sa.sa_family); 68 peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
69 memset(&peer->saddr, 0, sizeof(union sctp_addr)); 69 memset(&peer->saddr, 0, sizeof(union sctp_addr));
70 70
71 peer->sack_generation = 0;
72
71 /* From 6.3.1 RTO Calculation: 73 /* From 6.3.1 RTO Calculation:
72 * 74 *
73 * C1) Until an RTT measurement has been made for a packet sent to the 75 * C1) Until an RTT measurement has been made for a packet sent to the
diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c
index f1e40cebc981..b5fb7c409023 100644
--- a/net/sctp/tsnmap.c
+++ b/net/sctp/tsnmap.c
@@ -114,7 +114,8 @@ int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn)
114 114
115 115
116/* Mark this TSN as seen. */ 116/* Mark this TSN as seen. */
117int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn) 117int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn,
118 struct sctp_transport *trans)
118{ 119{
119 u16 gap; 120 u16 gap;
120 121
@@ -133,6 +134,9 @@ int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn)
133 */ 134 */
134 map->max_tsn_seen++; 135 map->max_tsn_seen++;
135 map->cumulative_tsn_ack_point++; 136 map->cumulative_tsn_ack_point++;
137 if (trans)
138 trans->sack_generation =
139 trans->asoc->peer.sack_generation;
136 map->base_tsn++; 140 map->base_tsn++;
137 } else { 141 } else {
138 /* Either we already have a gap, or about to record a gap, so 142 /* Either we already have a gap, or about to record a gap, so
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 8a84017834c2..33d894776192 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -715,7 +715,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
715 * can mark it as received so the tsn_map is updated correctly. 715 * can mark it as received so the tsn_map is updated correctly.
716 */ 716 */
717 if (sctp_tsnmap_mark(&asoc->peer.tsn_map, 717 if (sctp_tsnmap_mark(&asoc->peer.tsn_map,
718 ntohl(chunk->subh.data_hdr->tsn))) 718 ntohl(chunk->subh.data_hdr->tsn),
719 chunk->transport))
719 goto fail_mark; 720 goto fail_mark;
720 721
721 /* First calculate the padding, so we don't inadvertently 722 /* First calculate the padding, so we don't inadvertently
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index f2d1de7f2ffb..f5a6a4f4faf7 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -1051,7 +1051,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
1051 if (chunk && (freed >= needed)) { 1051 if (chunk && (freed >= needed)) {
1052 __u32 tsn; 1052 __u32 tsn;
1053 tsn = ntohl(chunk->subh.data_hdr->tsn); 1053 tsn = ntohl(chunk->subh.data_hdr->tsn);
1054 sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn); 1054 sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn, chunk->transport);
1055 sctp_ulpq_tail_data(ulpq, chunk, gfp); 1055 sctp_ulpq_tail_data(ulpq, chunk, gfp);
1056 1056
1057 sctp_ulpq_partial_delivery(ulpq, chunk, gfp); 1057 sctp_ulpq_partial_delivery(ulpq, chunk, gfp);