diff options
-rw-r--r-- | include/linux/ktime.h | 24 | ||||
-rw-r--r-- | include/net/sctp/structs.h | 6 | ||||
-rw-r--r-- | net/sctp/associola.c | 168 | ||||
-rw-r--r-- | net/sctp/endpointola.c | 2 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 2 | ||||
-rw-r--r-- | net/sctp/transport.c | 2 |
6 files changed, 136 insertions, 68 deletions
diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 31c0cd1c941a..de9e46e6bcc9 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h | |||
@@ -304,6 +304,30 @@ static inline int ktime_compare(const ktime_t cmp1, const ktime_t cmp2) | |||
304 | return 0; | 304 | return 0; |
305 | } | 305 | } |
306 | 306 | ||
307 | /** | ||
308 | * ktime_after - Compare if a ktime_t value is bigger than another one. | ||
309 | * @cmp1: comparable1 | ||
310 | * @cmp2: comparable2 | ||
311 | * | ||
312 | * Return: true if cmp1 happened after cmp2. | ||
313 | */ | ||
314 | static inline bool ktime_after(const ktime_t cmp1, const ktime_t cmp2) | ||
315 | { | ||
316 | return ktime_compare(cmp1, cmp2) > 0; | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * ktime_before - Compare if a ktime_t value is smaller than another one. | ||
321 | * @cmp1: comparable1 | ||
322 | * @cmp2: comparable2 | ||
323 | * | ||
324 | * Return: true if cmp1 happened before cmp2. | ||
325 | */ | ||
326 | static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2) | ||
327 | { | ||
328 | return ktime_compare(cmp1, cmp2) < 0; | ||
329 | } | ||
330 | |||
307 | static inline s64 ktime_to_us(const ktime_t kt) | 331 | static inline s64 ktime_to_us(const ktime_t kt) |
308 | { | 332 | { |
309 | struct timeval tv = ktime_to_timeval(kt); | 333 | struct timeval tv = ktime_to_timeval(kt); |
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 0dfcc92600e8..f38588bf3462 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -838,10 +838,10 @@ struct sctp_transport { | |||
838 | unsigned long sackdelay; | 838 | unsigned long sackdelay; |
839 | __u32 sackfreq; | 839 | __u32 sackfreq; |
840 | 840 | ||
841 | /* When was the last time (in jiffies) that we heard from this | 841 | /* When was the last time that we heard from this transport? We use |
842 | * transport? We use this to pick new active and retran paths. | 842 | * this to pick new active and retran paths. |
843 | */ | 843 | */ |
844 | unsigned long last_time_heard; | 844 | ktime_t last_time_heard; |
845 | 845 | ||
846 | /* Last time(in jiffies) when cwnd is reduced due to the congestion | 846 | /* Last time(in jiffies) when cwnd is reduced due to the congestion |
847 | * indication based on ECNE chunk. | 847 | * indication based on ECNE chunk. |
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 39579c3e0d14..9e0509ce2f84 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c | |||
@@ -55,6 +55,7 @@ | |||
55 | #include <net/sctp/sm.h> | 55 | #include <net/sctp/sm.h> |
56 | 56 | ||
57 | /* Forward declarations for internal functions. */ | 57 | /* Forward declarations for internal functions. */ |
58 | static void sctp_select_active_and_retran_path(struct sctp_association *asoc); | ||
58 | static void sctp_assoc_bh_rcv(struct work_struct *work); | 59 | static void sctp_assoc_bh_rcv(struct work_struct *work); |
59 | static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc); | 60 | static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc); |
60 | static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc); | 61 | static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc); |
@@ -774,9 +775,6 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
774 | sctp_transport_cmd_t command, | 775 | sctp_transport_cmd_t command, |
775 | sctp_sn_error_t error) | 776 | sctp_sn_error_t error) |
776 | { | 777 | { |
777 | struct sctp_transport *t = NULL; | ||
778 | struct sctp_transport *first; | ||
779 | struct sctp_transport *second; | ||
780 | struct sctp_ulpevent *event; | 778 | struct sctp_ulpevent *event; |
781 | struct sockaddr_storage addr; | 779 | struct sockaddr_storage addr; |
782 | int spc_state = 0; | 780 | int spc_state = 0; |
@@ -829,13 +827,14 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
829 | return; | 827 | return; |
830 | } | 828 | } |
831 | 829 | ||
832 | /* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the | 830 | /* Generate and send a SCTP_PEER_ADDR_CHANGE notification |
833 | * user. | 831 | * to the user. |
834 | */ | 832 | */ |
835 | if (ulp_notify) { | 833 | if (ulp_notify) { |
836 | memset(&addr, 0, sizeof(struct sockaddr_storage)); | 834 | memset(&addr, 0, sizeof(struct sockaddr_storage)); |
837 | memcpy(&addr, &transport->ipaddr, | 835 | memcpy(&addr, &transport->ipaddr, |
838 | transport->af_specific->sockaddr_len); | 836 | transport->af_specific->sockaddr_len); |
837 | |||
839 | event = sctp_ulpevent_make_peer_addr_change(asoc, &addr, | 838 | event = sctp_ulpevent_make_peer_addr_change(asoc, &addr, |
840 | 0, spc_state, error, GFP_ATOMIC); | 839 | 0, spc_state, error, GFP_ATOMIC); |
841 | if (event) | 840 | if (event) |
@@ -843,60 +842,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, | |||
843 | } | 842 | } |
844 | 843 | ||
845 | /* Select new active and retran paths. */ | 844 | /* Select new active and retran paths. */ |
846 | 845 | sctp_select_active_and_retran_path(asoc); | |
847 | /* Look for the two most recently used active transports. | ||
848 | * | ||
849 | * This code produces the wrong ordering whenever jiffies | ||
850 | * rolls over, but we still get usable transports, so we don't | ||
851 | * worry about it. | ||
852 | */ | ||
853 | first = NULL; second = NULL; | ||
854 | |||
855 | list_for_each_entry(t, &asoc->peer.transport_addr_list, | ||
856 | transports) { | ||
857 | |||
858 | if ((t->state == SCTP_INACTIVE) || | ||
859 | (t->state == SCTP_UNCONFIRMED) || | ||
860 | (t->state == SCTP_PF)) | ||
861 | continue; | ||
862 | if (!first || t->last_time_heard > first->last_time_heard) { | ||
863 | second = first; | ||
864 | first = t; | ||
865 | } else if (!second || | ||
866 | t->last_time_heard > second->last_time_heard) | ||
867 | second = t; | ||
868 | } | ||
869 | |||
870 | /* RFC 2960 6.4 Multi-Homed SCTP Endpoints | ||
871 | * | ||
872 | * By default, an endpoint should always transmit to the | ||
873 | * primary path, unless the SCTP user explicitly specifies the | ||
874 | * destination transport address (and possibly source | ||
875 | * transport address) to use. | ||
876 | * | ||
877 | * [If the primary is active but not most recent, bump the most | ||
878 | * recently used transport.] | ||
879 | */ | ||
880 | if (((asoc->peer.primary_path->state == SCTP_ACTIVE) || | ||
881 | (asoc->peer.primary_path->state == SCTP_UNKNOWN)) && | ||
882 | first != asoc->peer.primary_path) { | ||
883 | second = first; | ||
884 | first = asoc->peer.primary_path; | ||
885 | } | ||
886 | |||
887 | if (!second) | ||
888 | second = first; | ||
889 | /* If we failed to find a usable transport, just camp on the | ||
890 | * primary, even if it is inactive. | ||
891 | */ | ||
892 | if (!first) { | ||
893 | first = asoc->peer.primary_path; | ||
894 | second = asoc->peer.primary_path; | ||
895 | } | ||
896 | |||
897 | /* Set the active and retran transports. */ | ||
898 | asoc->peer.active_path = first; | ||
899 | asoc->peer.retran_path = second; | ||
900 | } | 846 | } |
901 | 847 | ||
902 | /* Hold a reference to an association. */ | 848 | /* Hold a reference to an association. */ |
@@ -1090,7 +1036,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) | |||
1090 | } | 1036 | } |
1091 | 1037 | ||
1092 | if (chunk->transport) | 1038 | if (chunk->transport) |
1093 | chunk->transport->last_time_heard = jiffies; | 1039 | chunk->transport->last_time_heard = ktime_get(); |
1094 | 1040 | ||
1095 | /* Run through the state machine. */ | 1041 | /* Run through the state machine. */ |
1096 | error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, | 1042 | error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, |
@@ -1278,13 +1224,41 @@ static u8 sctp_trans_score(const struct sctp_transport *trans) | |||
1278 | return sctp_trans_state_to_prio_map[trans->state]; | 1224 | return sctp_trans_state_to_prio_map[trans->state]; |
1279 | } | 1225 | } |
1280 | 1226 | ||
1227 | static struct sctp_transport *sctp_trans_elect_tie(struct sctp_transport *trans1, | ||
1228 | struct sctp_transport *trans2) | ||
1229 | { | ||
1230 | if (trans1->error_count > trans2->error_count) { | ||
1231 | return trans2; | ||
1232 | } else if (trans1->error_count == trans2->error_count && | ||
1233 | ktime_after(trans2->last_time_heard, | ||
1234 | trans1->last_time_heard)) { | ||
1235 | return trans2; | ||
1236 | } else { | ||
1237 | return trans1; | ||
1238 | } | ||
1239 | } | ||
1240 | |||
1281 | static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr, | 1241 | static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr, |
1282 | struct sctp_transport *best) | 1242 | struct sctp_transport *best) |
1283 | { | 1243 | { |
1244 | u8 score_curr, score_best; | ||
1245 | |||
1284 | if (best == NULL) | 1246 | if (best == NULL) |
1285 | return curr; | 1247 | return curr; |
1286 | 1248 | ||
1287 | return sctp_trans_score(curr) > sctp_trans_score(best) ? curr : best; | 1249 | score_curr = sctp_trans_score(curr); |
1250 | score_best = sctp_trans_score(best); | ||
1251 | |||
1252 | /* First, try a score-based selection if both transport states | ||
1253 | * differ. If we're in a tie, lets try to make a more clever | ||
1254 | * decision here based on error counts and last time heard. | ||
1255 | */ | ||
1256 | if (score_curr > score_best) | ||
1257 | return curr; | ||
1258 | else if (score_curr == score_best) | ||
1259 | return sctp_trans_elect_tie(curr, best); | ||
1260 | else | ||
1261 | return best; | ||
1288 | } | 1262 | } |
1289 | 1263 | ||
1290 | void sctp_assoc_update_retran_path(struct sctp_association *asoc) | 1264 | void sctp_assoc_update_retran_path(struct sctp_association *asoc) |
@@ -1325,6 +1299,76 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) | |||
1325 | __func__, asoc, &asoc->peer.retran_path->ipaddr.sa); | 1299 | __func__, asoc, &asoc->peer.retran_path->ipaddr.sa); |
1326 | } | 1300 | } |
1327 | 1301 | ||
1302 | static void sctp_select_active_and_retran_path(struct sctp_association *asoc) | ||
1303 | { | ||
1304 | struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL; | ||
1305 | struct sctp_transport *trans_pf = NULL; | ||
1306 | |||
1307 | /* Look for the two most recently used active transports. */ | ||
1308 | list_for_each_entry(trans, &asoc->peer.transport_addr_list, | ||
1309 | transports) { | ||
1310 | /* Skip uninteresting transports. */ | ||
1311 | if (trans->state == SCTP_INACTIVE || | ||
1312 | trans->state == SCTP_UNCONFIRMED) | ||
1313 | continue; | ||
1314 | /* Keep track of the best PF transport from our | ||
1315 | * list in case we don't find an active one. | ||
1316 | */ | ||
1317 | if (trans->state == SCTP_PF) { | ||
1318 | trans_pf = sctp_trans_elect_best(trans, trans_pf); | ||
1319 | continue; | ||
1320 | } | ||
1321 | /* For active transports, pick the most recent ones. */ | ||
1322 | if (trans_pri == NULL || | ||
1323 | ktime_after(trans->last_time_heard, | ||
1324 | trans_pri->last_time_heard)) { | ||
1325 | trans_sec = trans_pri; | ||
1326 | trans_pri = trans; | ||
1327 | } else if (trans_sec == NULL || | ||
1328 | ktime_after(trans->last_time_heard, | ||
1329 | trans_sec->last_time_heard)) { | ||
1330 | trans_sec = trans; | ||
1331 | } | ||
1332 | } | ||
1333 | |||
1334 | /* RFC 2960 6.4 Multi-Homed SCTP Endpoints | ||
1335 | * | ||
1336 | * By default, an endpoint should always transmit to the primary | ||
1337 | * path, unless the SCTP user explicitly specifies the | ||
1338 | * destination transport address (and possibly source transport | ||
1339 | * address) to use. [If the primary is active but not most recent, | ||
1340 | * bump the most recently used transport.] | ||
1341 | */ | ||
1342 | if ((asoc->peer.primary_path->state == SCTP_ACTIVE || | ||
1343 | asoc->peer.primary_path->state == SCTP_UNKNOWN) && | ||
1344 | asoc->peer.primary_path != trans_pri) { | ||
1345 | trans_sec = trans_pri; | ||
1346 | trans_pri = asoc->peer.primary_path; | ||
1347 | } | ||
1348 | |||
1349 | /* We did not find anything useful for a possible retransmission | ||
1350 | * path; either primary path that we found is the the same as | ||
1351 | * the current one, or we didn't generally find an active one. | ||
1352 | */ | ||
1353 | if (trans_sec == NULL) | ||
1354 | trans_sec = trans_pri; | ||
1355 | |||
1356 | /* If we failed to find a usable transport, just camp on the | ||
1357 | * primary or retran, even if they are inactive, if possible | ||
1358 | * pick a PF iff it's the better choice. | ||
1359 | */ | ||
1360 | if (trans_pri == NULL) { | ||
1361 | trans_pri = sctp_trans_elect_best(asoc->peer.primary_path, | ||
1362 | asoc->peer.retran_path); | ||
1363 | trans_pri = sctp_trans_elect_best(trans_pri, trans_pf); | ||
1364 | trans_sec = asoc->peer.primary_path; | ||
1365 | } | ||
1366 | |||
1367 | /* Set the active and retran transports. */ | ||
1368 | asoc->peer.active_path = trans_pri; | ||
1369 | asoc->peer.retran_path = trans_sec; | ||
1370 | } | ||
1371 | |||
1328 | struct sctp_transport * | 1372 | struct sctp_transport * |
1329 | sctp_assoc_choose_alter_transport(struct sctp_association *asoc, | 1373 | sctp_assoc_choose_alter_transport(struct sctp_association *asoc, |
1330 | struct sctp_transport *last_sent_to) | 1374 | struct sctp_transport *last_sent_to) |
@@ -1547,7 +1591,7 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc, | |||
1547 | /* Set an association id for a given association */ | 1591 | /* Set an association id for a given association */ |
1548 | int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) | 1592 | int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) |
1549 | { | 1593 | { |
1550 | bool preload = gfp & __GFP_WAIT; | 1594 | bool preload = !!(gfp & __GFP_WAIT); |
1551 | int ret; | 1595 | int ret; |
1552 | 1596 | ||
1553 | /* If the id is already assigned, keep it. */ | 1597 | /* If the id is already assigned, keep it. */ |
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 3d9f429858dc..9da76ba4d10f 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c | |||
@@ -481,7 +481,7 @@ normal: | |||
481 | } | 481 | } |
482 | 482 | ||
483 | if (chunk->transport) | 483 | if (chunk->transport) |
484 | chunk->transport->last_time_heard = jiffies; | 484 | chunk->transport->last_time_heard = ktime_get(); |
485 | 485 | ||
486 | error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state, | 486 | error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state, |
487 | ep, asoc, chunk, GFP_ATOMIC); | 487 | ep, asoc, chunk, GFP_ATOMIC); |
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index fee5552ddf92..ae0e616a7ca5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c | |||
@@ -1782,7 +1782,7 @@ no_hmac: | |||
1782 | else | 1782 | else |
1783 | kt = ktime_get(); | 1783 | kt = ktime_get(); |
1784 | 1784 | ||
1785 | if (!asoc && ktime_compare(bear_cookie->expiration, kt) < 0) { | 1785 | if (!asoc && ktime_before(bear_cookie->expiration, kt)) { |
1786 | /* | 1786 | /* |
1787 | * Section 3.3.10.3 Stale Cookie Error (3) | 1787 | * Section 3.3.10.3 Stale Cookie Error (3) |
1788 | * | 1788 | * |
diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 1d348d15b33d..7dd672fa651f 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c | |||
@@ -72,7 +72,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net, | |||
72 | */ | 72 | */ |
73 | peer->rto = msecs_to_jiffies(net->sctp.rto_initial); | 73 | peer->rto = msecs_to_jiffies(net->sctp.rto_initial); |
74 | 74 | ||
75 | peer->last_time_heard = jiffies; | 75 | peer->last_time_heard = ktime_get(); |
76 | peer->last_time_ecne_reduced = jiffies; | 76 | peer->last_time_ecne_reduced = jiffies; |
77 | 77 | ||
78 | peer->param_flags = SPP_HB_DISABLE | | 78 | peer->param_flags = SPP_HB_DISABLE | |