Commit 813ebbbf authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp-next'

Daniel Borkmann says:

====================
SCTP update

This set contains transport path selection improvements in
SCTP. Please see individual patches for details.
====================
Acked-by: default avatarVlad Yasevich <vyasevich@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9181a6bd 9b87d465
...@@ -304,6 +304,30 @@ static inline int ktime_compare(const ktime_t cmp1, const ktime_t cmp2) ...@@ -304,6 +304,30 @@ static inline int ktime_compare(const ktime_t cmp1, const ktime_t cmp2)
return 0; return 0;
} }
/**
* ktime_after - Compare if a ktime_t value is bigger than another one.
* @cmp1: comparable1
* @cmp2: comparable2
*
* Return: true if cmp1 happened after cmp2.
*/
static inline bool ktime_after(const ktime_t cmp1, const ktime_t cmp2)
{
return ktime_compare(cmp1, cmp2) > 0;
}
/**
* ktime_before - Compare if a ktime_t value is smaller than another one.
* @cmp1: comparable1
* @cmp2: comparable2
*
* Return: true if cmp1 happened before cmp2.
*/
static inline bool ktime_before(const ktime_t cmp1, const ktime_t cmp2)
{
return ktime_compare(cmp1, cmp2) < 0;
}
static inline s64 ktime_to_us(const ktime_t kt) static inline s64 ktime_to_us(const ktime_t kt)
{ {
struct timeval tv = ktime_to_timeval(kt); struct timeval tv = ktime_to_timeval(kt);
......
...@@ -838,10 +838,10 @@ struct sctp_transport { ...@@ -838,10 +838,10 @@ struct sctp_transport {
unsigned long sackdelay; unsigned long sackdelay;
__u32 sackfreq; __u32 sackfreq;
/* When was the last time (in jiffies) that we heard from this /* When was the last time that we heard from this transport? We use
* transport? We use this to pick new active and retran paths. * this to pick new active and retran paths.
*/ */
unsigned long last_time_heard; ktime_t last_time_heard;
/* Last time(in jiffies) when cwnd is reduced due to the congestion /* Last time(in jiffies) when cwnd is reduced due to the congestion
* indication based on ECNE chunk. * indication based on ECNE chunk.
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Forward declarations for internal functions. */ /* Forward declarations for internal functions. */
static void sctp_select_active_and_retran_path(struct sctp_association *asoc);
static void sctp_assoc_bh_rcv(struct work_struct *work); static void sctp_assoc_bh_rcv(struct work_struct *work);
static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc); static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc);
static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc); 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,9 +775,6 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
sctp_transport_cmd_t command, sctp_transport_cmd_t command,
sctp_sn_error_t error) sctp_sn_error_t error)
{ {
struct sctp_transport *t = NULL;
struct sctp_transport *first;
struct sctp_transport *second;
struct sctp_ulpevent *event; struct sctp_ulpevent *event;
struct sockaddr_storage addr; struct sockaddr_storage addr;
int spc_state = 0; int spc_state = 0;
...@@ -829,13 +827,14 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, ...@@ -829,13 +827,14 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
return; return;
} }
/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the /* Generate and send a SCTP_PEER_ADDR_CHANGE notification
* user. * to the user.
*/ */
if (ulp_notify) { if (ulp_notify) {
memset(&addr, 0, sizeof(struct sockaddr_storage)); memset(&addr, 0, sizeof(struct sockaddr_storage));
memcpy(&addr, &transport->ipaddr, memcpy(&addr, &transport->ipaddr,
transport->af_specific->sockaddr_len); transport->af_specific->sockaddr_len);
event = sctp_ulpevent_make_peer_addr_change(asoc, &addr, event = sctp_ulpevent_make_peer_addr_change(asoc, &addr,
0, spc_state, error, GFP_ATOMIC); 0, spc_state, error, GFP_ATOMIC);
if (event) if (event)
...@@ -843,60 +842,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc, ...@@ -843,60 +842,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
} }
/* Select new active and retran paths. */ /* Select new active and retran paths. */
sctp_select_active_and_retran_path(asoc);
/* Look for the two most recently used active transports.
*
* This code produces the wrong ordering whenever jiffies
* rolls over, but we still get usable transports, so we don't
* worry about it.
*/
first = NULL; second = NULL;
list_for_each_entry(t, &asoc->peer.transport_addr_list,
transports) {
if ((t->state == SCTP_INACTIVE) ||
(t->state == SCTP_UNCONFIRMED) ||
(t->state == SCTP_PF))
continue;
if (!first || t->last_time_heard > first->last_time_heard) {
second = first;
first = t;
} else if (!second ||
t->last_time_heard > second->last_time_heard)
second = t;
}
/* RFC 2960 6.4 Multi-Homed SCTP Endpoints
*
* By default, an endpoint should always transmit to the
* primary path, unless the SCTP user explicitly specifies the
* destination transport address (and possibly source
* transport address) to use.
*
* [If the primary is active but not most recent, bump the most
* recently used transport.]
*/
if (((asoc->peer.primary_path->state == SCTP_ACTIVE) ||
(asoc->peer.primary_path->state == SCTP_UNKNOWN)) &&
first != asoc->peer.primary_path) {
second = first;
first = asoc->peer.primary_path;
}
if (!second)
second = first;
/* If we failed to find a usable transport, just camp on the
* primary, even if it is inactive.
*/
if (!first) {
first = asoc->peer.primary_path;
second = asoc->peer.primary_path;
}
/* Set the active and retran transports. */
asoc->peer.active_path = first;
asoc->peer.retran_path = second;
} }
/* Hold a reference to an association. */ /* Hold a reference to an association. */
...@@ -1090,7 +1036,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) ...@@ -1090,7 +1036,7 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
} }
if (chunk->transport) if (chunk->transport)
chunk->transport->last_time_heard = jiffies; chunk->transport->last_time_heard = ktime_get();
/* Run through the state machine. */ /* Run through the state machine. */
error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, 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,13 +1224,41 @@ static u8 sctp_trans_score(const struct sctp_transport *trans)
return sctp_trans_state_to_prio_map[trans->state]; return sctp_trans_state_to_prio_map[trans->state];
} }
static struct sctp_transport *sctp_trans_elect_tie(struct sctp_transport *trans1,
struct sctp_transport *trans2)
{
if (trans1->error_count > trans2->error_count) {
return trans2;
} else if (trans1->error_count == trans2->error_count &&
ktime_after(trans2->last_time_heard,
trans1->last_time_heard)) {
return trans2;
} else {
return trans1;
}
}
static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr, static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
struct sctp_transport *best) struct sctp_transport *best)
{ {
u8 score_curr, score_best;
if (best == NULL) if (best == NULL)
return curr; return curr;
return sctp_trans_score(curr) > sctp_trans_score(best) ? curr : best; score_curr = sctp_trans_score(curr);
score_best = sctp_trans_score(best);
/* First, try a score-based selection if both transport states
* differ. If we're in a tie, lets try to make a more clever
* decision here based on error counts and last time heard.
*/
if (score_curr > score_best)
return curr;
else if (score_curr == score_best)
return sctp_trans_elect_tie(curr, best);
else
return best;
} }
void sctp_assoc_update_retran_path(struct sctp_association *asoc) 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,6 +1299,76 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
__func__, asoc, &asoc->peer.retran_path->ipaddr.sa); __func__, asoc, &asoc->peer.retran_path->ipaddr.sa);
} }
static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
{
struct sctp_transport *trans, *trans_pri = NULL, *trans_sec = NULL;
struct sctp_transport *trans_pf = NULL;
/* Look for the two most recently used active transports. */
list_for_each_entry(trans, &asoc->peer.transport_addr_list,
transports) {
/* Skip uninteresting transports. */
if (trans->state == SCTP_INACTIVE ||
trans->state == SCTP_UNCONFIRMED)
continue;
/* Keep track of the best PF transport from our
* list in case we don't find an active one.
*/
if (trans->state == SCTP_PF) {
trans_pf = sctp_trans_elect_best(trans, trans_pf);
continue;
}
/* For active transports, pick the most recent ones. */
if (trans_pri == NULL ||
ktime_after(trans->last_time_heard,
trans_pri->last_time_heard)) {
trans_sec = trans_pri;
trans_pri = trans;
} else if (trans_sec == NULL ||
ktime_after(trans->last_time_heard,
trans_sec->last_time_heard)) {
trans_sec = trans;
}
}
/* RFC 2960 6.4 Multi-Homed SCTP Endpoints
*
* By default, an endpoint should always transmit to the primary
* path, unless the SCTP user explicitly specifies the
* destination transport address (and possibly source transport
* address) to use. [If the primary is active but not most recent,
* bump the most recently used transport.]
*/
if ((asoc->peer.primary_path->state == SCTP_ACTIVE ||
asoc->peer.primary_path->state == SCTP_UNKNOWN) &&
asoc->peer.primary_path != trans_pri) {
trans_sec = trans_pri;
trans_pri = asoc->peer.primary_path;
}
/* We did not find anything useful for a possible retransmission
* path; either primary path that we found is the the same as
* the current one, or we didn't generally find an active one.
*/
if (trans_sec == NULL)
trans_sec = trans_pri;
/* If we failed to find a usable transport, just camp on the
* primary or retran, even if they are inactive, if possible
* pick a PF iff it's the better choice.
*/
if (trans_pri == NULL) {
trans_pri = sctp_trans_elect_best(asoc->peer.primary_path,
asoc->peer.retran_path);
trans_pri = sctp_trans_elect_best(trans_pri, trans_pf);
trans_sec = asoc->peer.primary_path;
}
/* Set the active and retran transports. */
asoc->peer.active_path = trans_pri;
asoc->peer.retran_path = trans_sec;
}
struct sctp_transport * struct sctp_transport *
sctp_assoc_choose_alter_transport(struct sctp_association *asoc, sctp_assoc_choose_alter_transport(struct sctp_association *asoc,
struct sctp_transport *last_sent_to) struct sctp_transport *last_sent_to)
...@@ -1547,7 +1591,7 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc, ...@@ -1547,7 +1591,7 @@ int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
/* Set an association id for a given association */ /* Set an association id for a given association */
int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp)
{ {
bool preload = gfp & __GFP_WAIT; bool preload = !!(gfp & __GFP_WAIT);
int ret; int ret;
/* If the id is already assigned, keep it. */ /* If the id is already assigned, keep it. */
......
...@@ -481,7 +481,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work) ...@@ -481,7 +481,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work)
} }
if (chunk->transport) if (chunk->transport)
chunk->transport->last_time_heard = jiffies; chunk->transport->last_time_heard = ktime_get();
error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state, error = sctp_do_sm(net, SCTP_EVENT_T_CHUNK, subtype, state,
ep, asoc, chunk, GFP_ATOMIC); ep, asoc, chunk, GFP_ATOMIC);
......
...@@ -1782,7 +1782,7 @@ struct sctp_association *sctp_unpack_cookie( ...@@ -1782,7 +1782,7 @@ struct sctp_association *sctp_unpack_cookie(
else else
kt = ktime_get(); kt = ktime_get();
if (!asoc && ktime_compare(bear_cookie->expiration, kt) < 0) { if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
/* /*
* Section 3.3.10.3 Stale Cookie Error (3) * Section 3.3.10.3 Stale Cookie Error (3)
* *
......
...@@ -72,7 +72,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net, ...@@ -72,7 +72,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net,
*/ */
peer->rto = msecs_to_jiffies(net->sctp.rto_initial); peer->rto = msecs_to_jiffies(net->sctp.rto_initial);
peer->last_time_heard = jiffies; peer->last_time_heard = ktime_get();
peer->last_time_ecne_reduced = jiffies; peer->last_time_ecne_reduced = jiffies;
peer->param_flags = SPP_HB_DISABLE | peer->param_flags = SPP_HB_DISABLE |
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment