Desinstale el módulo tcp_congestion_control en uso

Hace mucho tiempo, cuando estaba desarrollando un módulo de algoritmo de control de congestión TCP, debido a las frecuentes y rápidas iteraciones, a menudo era necesario descargar y reinstalar el módulo, y luego descargarlo. Debido al parámetro global del kernel de Linux net.ipv4.tcp_congestion_control cambié, I La conexión ssh también necesita usar el mismo módulo CC, lo que lleva al hecho de que después de cambiar el algoritmo predeterminado al algoritmo incorporado reno, todas las conexiones TCP deben desconectarse antes de que el recuento de referencias del módulo CC se pueda reducir a 0, desinstalando así sin problemas.

Tome bic como ejemplo. Después de cargar el módulo tcp_bic, me conecto a varias conexiones TCP:

[root@localhost ~]# sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = bic
[root@localhost ~]# lsmod |grep bic
tcp_bic                13483  3

Verá, el recuento de referencia de tcp_bic es 3, lo que significa que 3 conexiones están usando el algoritmo:

[root@localhost ~]# ss -antip|awk -F ' ' '/ bic/{print a;}{a=$6}'
users:(("sshd",pid=10323,fd=3))
users:(("sshd",pid=10515,fd=3))
users:(("sshd",pid=10348,fd=3))

¿Cómo desinstalar el módulo tcp_bic sin matar estos tres procesos?

Este artículo trata sobre esto, jaja.

La idea es muy simple, ¿no es suficiente con cambiar el algoritmo CC de estos tres procesos al algoritmo incorporado de reno?

¿Qué tan fácil es? Aunque socket admite TCP_CONGESTION este sockopt, debe establecerse en el contexto del proceso, que requiere un proceso de enlace dinámico. Estos tres procesos están en espera la mayor parte del tiempo. Podemos encontrar uno y echar un vistazo:

[root@localhost ~]# cat /proc/10348/wchan
poll_schedule_timeout
[root@localhost ~]#

Esto significa que necesitamos conectar llamadas como select / poll / epoll, y luego insertar la llamada setsockopt.

Complejo y poco interesante.

Déjame hacer malabares con un método basado en systemtap.

Completa dos objetivos:

  • Encuentre una manera de ir al contexto del proceso objetivo.
  • Setsockopt completo en el contexto del proceso objetivo.

Se alcanza el segundo objetivo, la pregunta es cómo lograr el primer objetivo.

No es dificil.

Sabemos que todos los procesos se cambian para ingresar desde __schedule, y se cambia de __schedule out, por lo que solo es necesario conectar __schedule.return y luego reactivar el proceso de destino.

Esto hará que se despierte el proceso objetivo de esperar en select / poll / epoll, y luego se cambie nuevamente después de verificar que el recurso no está listo.Nuestra posibilidad está en el medio, es decir, el momento en que __schedule regresa.

El código es el siguiente:

#!/usr/bin/stap -g

%{
    
    
#include <net/sock.h>
%}

function alter_cc(fd:long)
%{
    
    
	int err;
	mm_segment_t fs;
	char cc[] = "reno";
	struct socket *socket = NULL;

	socket = sockfd_lookup(STAP_ARG_fd, &err);
	if (socket == NULL) {
    
    
		return;
	}

	fs = get_fs();
	set_fs(KERNEL_DS);
#define TCP_CONGESTION	13
	sock_common_setsockopt(socket, SOL_TCP, TCP_CONGESTION, (void*)cc, strlen(cc));
	set_fs(fs);

	sockfd_put(socket);
%}

probe kernel.function("__schedule").return
{
    
    
	if (pid() == $1) {
    
    
		alter_cc($2);
		exit()
	}
}

function wakeup(pid:long)
%{
    
    
	struct task_struct *tsk;

	tsk = pid_task(find_vpid(STAP_ARG_pid), PIDTYPE_PID);
	if (tsk)
		wake_up_process(tsk);
%}

probe timer.ms(500)
{
    
    
	wakeup($1)
}

Sí, es así de simple. Ven y mira el efecto:

[root@localhost ~]# lsmod |grep tcp_bic
tcp_bic                13483  3
[root@localhost ~]# ss -antip|awk -F ' ' '/ bic/{print a;}{a=$6}'
users:(("sshd",pid=11707,fd=3))
users:(("sshd",pid=11680,fd=3))
users:(("sshd",pid=11654,fd=3))
[root@localhost ~]# ss -antip|awk -F ' ' '/ bic/{print a;}{a=$6}'|egrep -o [0-9]+ |sed 'N;s/\n/ /g' |xargs -L 1 ./alterCC.stp
[root@localhost ~]# ss -antip|awk -F ' ' '/ bic/{print a;}{a=$6}'
[root@localhost ~]# lsmod |grep tcp_bic
tcp_bic                13483  0
[root@localhost ~]# rmmod tcp_bic
[root@localhost ~]# echo $?
0
[root@localhost ~]# lsmod |grep tcp_bic

Se hizo de una vez, no hay mucho que decir.

Puede confirmar si el algoritmo CC de estos procesos se ha cambiado a reno, así que utilizo reno para que coincida:

[root@localhost ~]# ss -antip|awk -F ' ' '/reno/{print a;}{a=$6}'
users:(("sshd",pid=11707,fd=3))
users:(("sshd",pid=11680,fd=3))
users:(("sshd",pid=11654,fd=3))

Bueno, admito que el ss / egrep / sed / xargs aquí es un poco bajo. Si alguien puede ayudarme a escribir uno elegante, estoy agradecido.

Luego, haciendo malabarismos con un complemento de extensión de bloqueo para atravesar el algoritmo CC de todas las conexiones TCP, sí, el código es el siguiente:


static int get_field(unsigned long addr, char *name, char *field, void* buf)
{
    
    
	unsigned long off = MEMBER_OFFSET(name, field);

	if (!readmem(addr + off, KVADDR, buf, MEMBER_SIZE(name, field), name, FAULT_ON_ERROR))
		return 0;
	return 1;
}

struct dummy_list {
    
    
	struct dummy_list *stub;
};

struct dummy_list *iter;
void do_cmd(void)
{
    
    
	unsigned long _addr, sock_addr, cc_addr, socket_addr, inode_addr, next;
	unsigned long ehash_mask = 0, inode;
	char name[64];
	int i;

	optind++;
	iter = (struct dummy_list *)htol(args[optind], FAULT_ON_ERROR, NULL);
	optind++;
	ehash_mask = atoi(args[optind]);

	for (i = 0; i <= ehash_mask; i++) {
    
    
		next = (unsigned long)iter + i*sizeof(unsigned long);
		do {
    
    
			inode = 0;
			get_field(next, "hlist_nulls_node", "next", &_addr);
			if (_addr & 0x1) {
    
    
				break;
			}
			sock_addr = _addr - MEMBER_OFFSET("sock_common", "skc_nulls_node");
			get_field(sock_addr, "inet_connection_sock", "icsk_ca_ops", &cc_addr);
			get_field(cc_addr, "tcp_congestion_ops", "name", &name[0]);
			get_field(sock_addr, "sock", "sk_socket", &socket_addr);
			if (socket_addr) {
    
    
				inode_addr = socket_addr + MEMBER_OFFSET("socket_alloc", "vfs_inode");
				get_field(inode_addr, "inode", "i_ino", &inode);
			}
			fprintf(fp, "  ----%s   %d\n", name, inode);
		} while(get_field(next, "hlist_nulls_node", "next", &next));
	}
}

static struct command_table_entry command_table[] = {
    
    
	{
    
     "tcpcc", do_cmd, NULL, 0},
	{
    
     NULL },
};

void __attribute__((constructor)) tcpcc_init(void)
{
    
    
	register_extension(command_table);
}

void __attribute__((destructor)) tcpcc_fini(void) {
    
     }

Compílelo:

[root@localhost ext]# gcc -fPIC -shared tcpcc.c -o tcpcc.so

Cárguelo en la línea de comandos de bloqueo y ejecútelo:

crash>
crash> extend  tcpcc.so
./tcpcc.so: shared object loaded
crash> px tcp_hashinfo.ehash
$5 = (struct inet_ehash_bucket *) 0xffffc90000188000
crash> pd tcp_hashinfo.ehash_mask
$6 = 8191
crash>
crash> tcpcc 0xffffc90000188000 8191
  ----reno   27670
  ----reno   27547
  ----reno   27407
  ----reno   36423
  ----reno   28717
  ----reno   36282
  ----reno   36138
crash>

La segunda columna de la salida anterior es el número de inodo del socket, que se usa para hacer coincidir el fd de la tarea en procfs, y la intersección final coopera con el pid para ejecutar el script alterCC.stp.

Por desgracia, será otra terrible combinación de comandos.


Los zapatos de cuero en Wenzhou, Zhejiang están mojados, por lo que no engordan con la lluvia.

Supongo que te gusta

Origin blog.csdn.net/dog250/article/details/108089212
Recomendado
Clasificación