Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+24 −0
Original line number Original line Diff line number Diff line
@@ -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);
+3 −3
Original line number Original line Diff line number Diff line
@@ -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.
+106 −62
Original line number Original line Diff line number Diff line
@@ -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,
				  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,
		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,
	}
	}


	/* 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)
		}
		}


		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)
	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)
		 __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,
/* 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. */
+1 −1
Original line number Original line Diff line number Diff line
@@ -481,7 +481,7 @@ normal:
		}
		}


		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);
+1 −1
Original line number Original line Diff line number Diff line
@@ -1782,7 +1782,7 @@ no_hmac:
	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)
		 *
		 *
Loading