aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2014-06-11 12:19:31 -0400
committerDavid S. Miller <davem@davemloft.net>2014-06-11 15:23:17 -0400
commita7288c4dd5094b3878b7ad817b0cd130a6f8e916 (patch)
tree0d70eaf2c24c7824f942a0aad61e721a7b82d19f /net
parente575235fc6026bb75e166ff68f84118c62d73f94 (diff)
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 <dborkman@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/sctp/associola.c50
1 files changed, 45 insertions, 5 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 620c99e19e77..58bbb731fd26 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1224,13 +1224,41 @@ static u8 sctp_trans_score(const struct sctp_transport *trans)
1224 return sctp_trans_state_to_prio_map[trans->state]; 1224 return sctp_trans_state_to_prio_map[trans->state];
1225} 1225}
1226 1226
1227static 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
1227static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr, 1241static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
1228 struct sctp_transport *best) 1242 struct sctp_transport *best)
1229{ 1243{
1244 u8 score_curr, score_best;
1245
1230 if (best == NULL) 1246 if (best == NULL)
1231 return curr; 1247 return curr;
1232 1248
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;
1234} 1262}
1235 1263
1236void sctp_assoc_update_retran_path(struct sctp_association *asoc) 1264void sctp_assoc_update_retran_path(struct sctp_association *asoc)
@@ -1274,14 +1302,23 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
1274static void sctp_select_active_and_retran_path(struct sctp_association *asoc) 1302static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
1275{ 1303{
1276 struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL; 1304 struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL;
1305 struct sctp_transport *trans_pf = NULL;
1277 1306
1278 /* Look for the two most recently used active transports. */ 1307 /* Look for the two most recently used active transports. */
1279 list_for_each_entry(trans, &asoc->peer.transport_addr_list, 1308 list_for_each_entry(trans, &asoc->peer.transport_addr_list,
1280 transports) { 1309 transports) {
1310 /* Skip uninteresting transports. */
1281 if (trans->state == SCTP_INACTIVE || 1311 if (trans->state == SCTP_INACTIVE ||
1282 trans->state == SCTP_UNCONFIRMED || 1312 trans->state == SCTP_UNCONFIRMED)
1283 trans->state == SCTP_PF)
1284 continue; 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. */
1285 if (trans_pri == NULL || 1322 if (trans_pri == NULL ||
1286 ktime_after(trans->last_time_heard, 1323 ktime_after(trans->last_time_heard,
1287 trans_pri->last_time_heard)) { 1324 trans_pri->last_time_heard)) {
@@ -1317,10 +1354,13 @@ static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
1317 trans_sec = trans_pri; 1354 trans_sec = trans_pri;
1318 1355
1319 /* If we failed to find a usable transport, just camp on the 1356 /* 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.
1321 */ 1359 */
1322 if (trans_pri == NULL) { 1360 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);
1324 trans_sec = asoc->peer.primary_path; 1364 trans_sec = asoc->peer.primary_path;
1325 } 1365 }
1326 1366