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

Commit 91b5b21c authored by Lawrence Brakmo's avatar Lawrence Brakmo Committed by David S. Miller
Browse files

bpf: Add support for changing congestion control



Added support for changing congestion control for SOCK_OPS bpf
programs through the setsockopt bpf helper function. It also adds
a new SOCK_OPS op, BPF_SOCK_OPS_NEEDS_ECN, that is needed for
congestion controls, like dctcp, that need to enable ECN in the
SYN packets.

Signed-off-by: default avatarLawrence Brakmo <brakmo@fb.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d9925368
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -1004,7 +1004,9 @@ void tcp_get_default_congestion_control(char *name);
void tcp_get_available_congestion_control(char *buf, size_t len);
void tcp_get_allowed_congestion_control(char *buf, size_t len);
int tcp_set_allowed_congestion_control(char *allowed);
int tcp_set_congestion_control(struct sock *sk, const char *name);
int tcp_set_congestion_control(struct sock *sk, const char *name, bool load);
void tcp_reinit_congestion_control(struct sock *sk,
				   const struct tcp_congestion_ops *ca);
u32 tcp_slow_start(struct tcp_sock *tp, u32 acked);
void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked);

@@ -2078,4 +2080,9 @@ static inline u32 tcp_rwnd_init_bpf(struct sock *sk)
		rwnd = 0;
	return rwnd;
}

static inline bool tcp_bpf_ca_needs_ecn(struct sock *sk)
{
	return (tcp_call_bpf(sk, BPF_SOCK_OPS_NEEDS_ECN) == 1);
}
#endif	/* _TCP_H */
+3 −0
Original line number Diff line number Diff line
@@ -778,6 +778,9 @@ enum {
						 * passive connection is
						 * established
						 */
	BPF_SOCK_OPS_NEEDS_ECN,		/* If connection's congestion control
					 * needs ECN
					 */
};

#endif /* _UAPI__LINUX_BPF_H__ */
+17 −1
Original line number Diff line number Diff line
@@ -2719,8 +2719,24 @@ BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
		}
	} else if (level == SOL_TCP &&
		   sk->sk_prot->setsockopt == tcp_setsockopt) {
		/* Place holder */
#ifdef CONFIG_INET
		if (optname == TCP_CONGESTION) {
			char name[TCP_CA_NAME_MAX];

			strncpy(name, optval, min_t(long, optlen,
						    TCP_CA_NAME_MAX-1));
			name[TCP_CA_NAME_MAX-1] = 0;
			ret = tcp_set_congestion_control(sk, name, false);
			if (!ret && bpf_sock->op > BPF_SOCK_OPS_NEEDS_ECN)
				/* replacing an existing ca */
				tcp_reinit_congestion_control(sk,
					inet_csk(sk)->icsk_ca_ops);
		} else {
			ret = -EINVAL;
		}
#else
		ret = -EINVAL;
#endif
	} else {
		ret = -EINVAL;
	}
+1 −1
Original line number Diff line number Diff line
@@ -2481,7 +2481,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
		name[val] = 0;

		lock_sock(sk);
		err = tcp_set_congestion_control(sk, name);
		err = tcp_set_congestion_control(sk, name, true);
		release_sock(sk);
		return err;
	}
+22 −10
Original line number Diff line number Diff line
@@ -189,7 +189,7 @@ void tcp_init_congestion_control(struct sock *sk)
		INET_ECN_dontxmit(sk);
}

static void tcp_reinit_congestion_control(struct sock *sk,
void tcp_reinit_congestion_control(struct sock *sk,
				   const struct tcp_congestion_ops *ca)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
@@ -333,8 +333,12 @@ int tcp_set_allowed_congestion_control(char *val)
	return ret;
}

/* Change congestion control for socket */
int tcp_set_congestion_control(struct sock *sk, const char *name)
/* Change congestion control for socket. If load is false, then it is the
 * responsibility of the caller to call tcp_init_congestion_control or
 * tcp_reinit_congestion_control (if the current congestion control was
 * already initialized.
 */
int tcp_set_congestion_control(struct sock *sk, const char *name, bool load)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	const struct tcp_congestion_ops *ca;
@@ -344,21 +348,29 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
		return -EPERM;

	rcu_read_lock();
	if (!load)
		ca = tcp_ca_find(name);
	else
		ca = __tcp_ca_find_autoload(name);
	/* No change asking for existing value */
	if (ca == icsk->icsk_ca_ops) {
		icsk->icsk_ca_setsockopt = 1;
		goto out;
	}
	if (!ca)
	if (!ca) {
		err = -ENOENT;
	else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) ||
		   ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)))
	} else if (!load) {
		icsk->icsk_ca_ops = ca;
		if (!try_module_get(ca->owner))
			err = -EBUSY;
	} else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) ||
		     ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))) {
		err = -EPERM;
	else if (!try_module_get(ca->owner))
	} else if (!try_module_get(ca->owner)) {
		err = -EBUSY;
	else
	} else {
		tcp_reinit_congestion_control(sk, ca);
	}
 out:
	rcu_read_unlock();
	return err;
Loading