Skip to content

Commit a7288c4

Browse files
Daniel Borkmanndavem330
authored andcommitted
net: sctp: improve sctp_select_active_and_retran_path selection
In function sctp_select_active_and_retran_path(), we walk the transport list in order to look for the two most recently used ACTIVE transports (trans_pri, trans_sec). In case we didn't find anything ACTIVE, we currently just camp on a possibly PF or INACTIVE transport that is primary path; this behavior actually dates back to linux-history tree of the very early days of lksctp, and can yield a behavior that chooses suboptimal transport paths. Instead, be a bit more clever by reusing and extending the recently introduced sctp_trans_elect_best() handler. In case both transports are evaluated to have the same score resulting from their states, break the tie by looking at: 1) transport patch error count 2) last_time_heard value from each transport. This is analogous to Nishida's Quick Failover draft [1], section 5.1, 3: The sender SHOULD avoid data transmission to PF destinations. When all destinations are in either PF or Inactive state, the sender MAY either move the destination from PF to active state (and transmit data to the active destination) or the sender MAY transmit data to a PF destination. In the former scenario, (i) the sender MUST NOT notify the ULP about the state transition, and (ii) MUST NOT clear the destination's error counter. It is recommended that the sender picks the PF destination with least error count (fewest consecutive timeouts) for data transmission. In case of a tie (multiple PF destinations with same error count), the sender MAY choose the last active destination. Thus for sctp_select_active_and_retran_path(), we keep track of the best, if any, transport that is in PF state and in case no ACTIVE transport has been found (hence trans_{pri,sec} is NULL), we select the best out of the three: current primary_path and retran_path as well as a possible PF transport. The secondary may still camp on the original primary_path as before. The change in sctp_trans_elect_best() with a more fine grained tie selection also improves at the same time path selection for sctp_assoc_update_retran_path() in case of non-ACTIVE states. [1] http://tools.ietf.org/html/draft-nishida-tsvwg-sctp-failover-05 Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent e575235 commit a7288c4

File tree

1 file changed

+45
-5
lines changed

1 file changed

+45
-5
lines changed

net/sctp/associola.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,13 +1224,41 @@ static u8 sctp_trans_score(const struct sctp_transport *trans)
12241224
return sctp_trans_state_to_prio_map[trans->state];
12251225
}
12261226

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+
12271241
static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
12281242
struct sctp_transport *best)
12291243
{
1244+
u8 score_curr, score_best;
1245+
12301246
if (best == NULL)
12311247
return curr;
12321248

1233-
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;
12341262
}
12351263

12361264
void sctp_assoc_update_retran_path(struct sctp_association *asoc)
@@ -1274,14 +1302,23 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
12741302
static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
12751303
{
12761304
struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL;
1305+
struct sctp_transport *trans_pf = NULL;
12771306

12781307
/* Look for the two most recently used active transports. */
12791308
list_for_each_entry(trans, &asoc->peer.transport_addr_list,
12801309
transports) {
1310+
/* Skip uninteresting transports. */
12811311
if (trans->state == SCTP_INACTIVE ||
1282-
trans->state == SCTP_UNCONFIRMED ||
1283-
trans->state == SCTP_PF)
1312+
trans->state == SCTP_UNCONFIRMED)
12841313
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. */
12851322
if (trans_pri == NULL ||
12861323
ktime_after(trans->last_time_heard,
12871324
trans_pri->last_time_heard)) {
@@ -1317,10 +1354,13 @@ static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
13171354
trans_sec = trans_pri;
13181355

13191356
/* If we failed to find a usable transport, just camp on the
1320-
* primary, even if they are inactive.
1357+
* primary or retran, even if they are inactive, if possible
1358+
* pick a PF iff it's the better choice.
13211359
*/
13221360
if (trans_pri == NULL) {
1323-
trans_pri = asoc->peer.primary_path;
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);
13241364
trans_sec = asoc->peer.primary_path;
13251365
}
13261366

0 commit comments

Comments
 (0)