Tabla de contenido
1 Estructura de datos tcp_congestion_ops
2.1 Algoritmo de control de congestión de la memoria residente
3.1 Registro / cancelación del algoritmo tcp_register_congestion_control
3.2 algoritmo de control de congestión especificado por socket tcp_set_congestion_control
3.3 Establecer el algoritmo predeterminado tcp_set_default_congestion_control
El kernel de Linux admite la coexistencia de múltiples algoritmos de control de congestión y admite el uso de diferentes algoritmos de control de congestión para diferentes flujos de TCP.
1 Estructura de datos tcp_congestion_ops
Cada algoritmo de control de congestión debe proporcionar una estructura struct tcp_congestion_ops, y luego registrarse con el sistema, y el sistema organiza todos los algoritmos de control de congestión registrados en una lista enlazada individualmente.
/*
* Interface for adding new TCP congestion control handlers
*/
#define TCP_CA_NAME_MAX 16
#define TCP_CA_MAX 128
#define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX)
#define TCP_CONG_NON_RESTRICTED 0x1
#define TCP_CONG_RTT_STAMP 0x2
struct tcp_congestion_ops {
//所有拥塞控制算法组织成单链表
struct list_head list;
//当前只定义了两个标记:
//TCP_CONG_NON_RESTRICTED: 如果没有设置该标记,表示算法的使用者需要有网络管理权限
//TCP_CONG_RTT_STAMP:
unsigned long flags;
/* initialize private data (optional) */
void (*init)(struct sock *sk);
/* cleanup private data (optional) */
void (*release)(struct sock *sk);
/* return slow start threshold (required) */
u32 (*ssthresh)(struct sock *sk);
/* lower bound for congestion window (optional) */
u32 (*min_cwnd)(const struct sock *sk);
/* do new cwnd calculation (required) */
void (*cong_avoid)(struct sock *sk, u32 ack, u32 in_flight);
/* call before changing ca_state (optional) */
void (*set_state)(struct sock *sk, u8 new_state);
/* call when cwnd event occurs (optional) */
void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev);
/* new value of cwnd after loss (optional) */
u32 (*undo_cwnd)(struct sock *sk);
/* hook for packet ack accounting (optional) */
void (*pkts_acked)(struct sock *sk, u32 num_acked, s32 rtt_us);
/* get info for inet_diag (optional) */
void (*get_info)(struct sock *sk, u32 ext, struct sk_buff *skb);
//可以为每个拥塞控制算法提供一个名字
char name[TCP_CA_NAME_MAX];
struct module *owner;
};
2 Inicializar tcp_cong_list
En primer lugar, todos los algoritmos de control de la congestión se organizan en una lista enlazada individualmente. La definición de la lista enlazada es la siguiente:
static DEFINE_SPINLOCK(tcp_cong_list_lock);
static LIST_HEAD(tcp_cong_list);
2.1 Algoritmo de control de congestión de la memoria residente
Cuando el sistema se inicia, tcp_init () registrará Reno (en realidad New Reno ) como el algoritmo de control de congestión predeterminado. Por supuesto, hay otras formas de cambiar esta configuración, pero en cualquier caso, New Reno se compila en la imagen del kernel y pueden existir otros algoritmos como módulos, que pueden cargarse dinámicamente cuando sea necesario.
void __init tcp_init(void)
{
...
tcp_register_congestion_control(&tcp_reno);
...
}
3 Operación básica
3.1 Registro / cancelación del algoritmo tcp_register_congestion_control
Como se describe en tcp_init (), el algoritmo de control de congestión puede registrarse con el kernel a través de la interfaz tcp_register_congestion_control ().
/*
* Attach new congestion control algorithm to the list
* of available options.
*/
入参就是拥塞控制算法
int tcp_register_congestion_control(struct tcp_congestion_ops *ca)
{
int ret = 0;
//ssthresh()和cong_avoid()回调是必须提供的
if (!ca->ssthresh || !ca->cong_avoid) {
printk(KERN_ERR "TCP %s does not implement required ops\n",
ca->name);
return -EINVAL;
}
spin_lock(&tcp_cong_list_lock);
//如果算法已经注册,那么注册失败
if (tcp_ca_find(ca->name)) {
printk(KERN_NOTICE "TCP %s already registered\n", ca->name);
ret = -EEXIST;
} else {
//将注册的算法添加到算法列表的末尾
list_add_tail_rcu(&ca->list, &tcp_cong_list);
printk(KERN_INFO "TCP %s registered\n", ca->name);
}
spin_unlock(&tcp_cong_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(tcp_register_congestion_control);
De manera similar, la cancelación se logra a través de tcp_unregister_congestion_control (), que no aparece aquí.
3.2 algoritmo de control de congestión especificado por socket tcp_set_congestion_control
El algoritmo de control de congestión se puede configurar para un socket a través de tcp_set_congestion_control (). El programa de espacio de usuario puede configurar el algoritmo de control de congestión para el socket a través de la opción TCP TCP_CONGESTION, y esta función es finalmente llamada.
/* Change congestion control for socket */
int tcp_set_congestion_control(struct sock *sk, const char *name)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_congestion_ops *ca;
int err = 0;
rcu_read_lock();
//查找算法是否已经注册
ca = tcp_ca_find(name);
/* no change asking for existing value */
//如果当前TCB的算法就是要设定的,直接返回
if (ca == icsk->icsk_ca_ops)
goto out;
#ifdef CONFIG_KMOD
//算法尚未注册,但是支持模块的动态加载,先尝试加载指定算法
/* not found attempt to autoload module */
if (!ca && capable(CAP_SYS_MODULE)) {
rcu_read_unlock();
request_module("tcp_%s", name);
rcu_read_lock();
ca = tcp_ca_find(name);
}
#endif
//最终还是没有找到指定的算法,失败
if (!ca)
err = -ENOENT;
//如果算法的使用是受限的,但是当前进程又没有网络管理权限,失败
else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) || capable(CAP_NET_ADMIN)))
err = -EPERM;
//获取不到引用计数,失败
else if (!try_module_get(ca->owner))
err = -EBUSY;
//设定成功
else {
//清理TCB中之前的拥塞控制算法
tcp_cleanup_congestion_control(sk);
//设置算法
icsk->icsk_ca_ops = ca;
//如果算法有提供初始化函数,调用初始化函数
if (sk->sk_state != TCP_CLOSE && icsk->icsk_ca_ops->init)
icsk->icsk_ca_ops->init(sk);
}
out:
rcu_read_unlock();
return err;
}
/* Manage refcounts on socket close. */
void tcp_cleanup_congestion_control(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
//调用release()回调
if (icsk->icsk_ca_ops->release)
icsk->icsk_ca_ops->release(sk);
//释放对算法的引用计数
module_put(icsk->icsk_ca_ops->owner);
}
3.3 Establecer el algoritmo predeterminado tcp_set_default_congestion_control
Puede llamar a tcp_set_default_congestion_control () para establecer el algoritmo de control de congestión predeterminado para el sistema.
/* Used by sysctl to change default congestion control */
int tcp_set_default_congestion_control(const char *name)
{
struct tcp_congestion_ops *ca;
int ret = -ENOENT;
spin_lock(&tcp_cong_list_lock);
//类似tcp_set_congestion_control()中的算法查找部分
ca = tcp_ca_find(name);
#ifdef CONFIG_KMOD
if (!ca && capable(CAP_SYS_MODULE)) {
spin_unlock(&tcp_cong_list_lock);
request_module("tcp_%s", name);
spin_lock(&tcp_cong_list_lock);
ca = tcp_ca_find(name);
}
#endif
if (ca) {
//默认的算法没有使用权限限制
ca->flags |= TCP_CONG_NON_RESTRICTED; /* default is always allowed */
//链表中第一个算法就是默认算法
list_move(&ca->list, &tcp_cong_list);
ret = 0;
}
spin_unlock(&tcp_cong_list_lock);
return ret;
}
/* Set default value from kernel configuration at bootup */
static int __init tcp_congestion_default(void)
{
//可以通过配置CONFIG_DEFAULT_TCP_CONG为系统设置默认的拥塞控制算法
return tcp_set_default_congestion_control(CONFIG_DEFAULT_TCP_CONG);
}
late_initcall(tcp_congestion_default);
Nota: De hecho, existen algunas otras operaciones comunes, pero son relativamente simples.