Algunas cosas en las que pensó spin_lock_bh

Recientemente, alguien me preguntó por qué necesito usar spin_lock / unlock_bh en lugar de spin_lock / unlock para proteger la sección crítica en la función del punto NF HOOK de PREROUTING.

Al enfrentar este problema, estaba un poco confundido. Cuando se trata de la familia spin_lock, hay muchas series de interfaces:

  • spin_lock / spin_unlock
  • spin_lock_bh / spin_unlock_bh
  • spin_lock_irq / spin_unlock_irq
  • spin_lock_irqsave / spin_unlock_irqrestore
  • ...

La razón por la que hay tantos, significa que con el fin de evitar la toma cerrada, el área crítica es interrumpida por una secuencia de alta prioridad de la misma CPU y un punto muerto reentrante.

Pero aún hay que dar un caso concreto para ser convincente, no solo en teoría.

De hecho, solo necesita dar un caso en el que el contexto del proceso llame a la función PREROUTING:

  1. El contexto de proceso C1 llama a spin_lock (Lx) en la función PREROUTING para ingresar a la sección crítica.
  2. La sección crítica no ha salido, la CPU que se ejecuta en C1 se interrumpe y luego se programa softirq para ejecutar net_rx_action.
  3. Ingrese a la función PREROUTING en el contexto de interrupción suave C2 y llame a spin_lock (Lx) para intentar ingresar a la sección crítica.
  4. Dado que C1 ha adquirido spinlock Lx, C2 comienza a girar, esperando que C1 libere Lx.
  5. Dado que C1 es reemplazado por C2, y C2 ya está girando, ¡está correctamente bloqueado!

Pero la pregunta es, ¿bajo qué circunstancias el contexto del proceso puede llegar a PREROUTING? ?

Recuerdo que fue por esta época en 2015, y escribí un artículo:
https://blog.csdn.net/dog250/article/details/48770481
El caso de este artículo es un escenario en el que la recepción de paquetes de datos se realiza en el contexto de un proceso y la recepción de paquetes de datos Debe haber pasado por el punto PREROUTING en el proceso.

Permítanme extraer una descripción del artículo:

Un paquete de datos TCP conectado a la máquina local finalmente alcanza la función de envío xmit de loopback, que simplemente programa un procesamiento de interrupción suave en la CPU, y luego programa su ejecución después de que termine la siguiente interrupción. Esto tiene una alta probabilidad de ser actual En el contexto del proceso de envío, es decir, el proceso de envío realizó la operación de envío en su contexto, y en este momento la interrupción suave tomó prestado su contexto para desencadenar la operación de recepción, ...

Sin embargo, hay un problema, lo que se quiere decir con "es probable que esto se lleve a cabo en el contexto del proceso de envío actual". Creo que esto no es riguroso, así que hoy quiero explorar este tema en profundidad:

  • ¿Por qué la lógica de envío y recepción de la tarjeta de red de bucle invertido se realiza en el mismo contexto de proceso?

Por esta razón, necesita imprimir la pila cuando haga ping a la comunicación a través de loopback localmente:

#!/usr/local/bin/stap -g

function dump()
%{
    
    
	dump_stack();
%}

probe kernel.function("icmp_rcv")
{
    
    
	dump();
	//print_backtrace(); // 这个不知为何不好使..
}

El siguiente es el resultado después de un ping:

[34197.319729]  [<ffffffff8159a145>] ? icmp_rcv+0x5/0x380
[34197.319732]  [<ffffffff81561b84>] ? ip_local_deliver_finish+0xb4/0x1f0
[34197.319735]  [<ffffffff81561e69>] ip_local_deliver+0x59/0xd0
[34197.319738]  [<ffffffff81561ad0>] ? ip_rcv_finish+0x350/0x350
[34197.319741]  [<ffffffff815617fd>] ip_rcv_finish+0x7d/0x350
[34197.319744]  [<ffffffff81562196>] ip_rcv+0x2b6/0x410
[34197.319747]  [<ffffffff81561780>] ? inet_del_offload+0x40/0x40
[34197.319752]  [<ffffffff815267b2>] __netif_receive_skb_core+0x582/0x7d0
[34197.319755]  [<ffffffff81526a18>] __netif_receive_skb+0x18/0x60
[34197.319757]  [<ffffffff815276ee>] process_backlog+0xae/0x180
[34197.319760]  [<ffffffff81526ed2>] net_rx_action+0x152/0x240
[34197.319765]  [<ffffffff8107e02f>] __do_softirq+0xef/0x280
[34197.319768]  [<ffffffff81646b1c>] call_softirq+0x1c/0x30
[34197.319769]  <EOI>  [<ffffffff81017155>] do_softirq+0x65/0xa0
[34197.319777]  [<ffffffff8107d924>] local_bh_enable+0x94/0xa0
[34197.319780]  [<ffffffff81566a00>] ip_finish_output+0x1f0/0x7d0
[34197.319783]  [<ffffffff81567cff>] ip_output+0x6f/0xe0
[34197.319786]  [<ffffffff81566810>] ? ip_fragment+0x8b0/0x8b0
[34197.319789]  [<ffffffff81565971>] ip_local_out_sk+0x31/0x40
[34197.319791]  [<ffffffff81568746>] ip_send_skb+0x16/0x50
[34197.319793]  [<ffffffff815687b3>] ip_push_pending_frames+0x33/0x40
[34197.319797]  [<ffffffff81590fbe>] raw_sendmsg+0x59e/0x620
[34197.319802]  [<ffffffff810af1a9>] ? ttwu_do_wakeup+0x19/0xd0
[34197.319805]  [<ffffffff8159f604>] inet_sendmsg+0x64/0xb0
[34197.319811]  [<ffffffff8150cc90>] sock_sendmsg+0xb0/0xf0
[34197.319814]  [<ffffffff8150d201>] SYSC_sendto+0x121/0x1c0
[34197.319817]  [<ffffffff8150e221>] ? __sys_recvmsg+0x51/0x90
[34197.319820]  [<ffffffff8150dc8e>] SyS_sendto+0xe/0x10
[34197.319823]  [<ffffffff81645189>] system_call_fastpath+0x16/0x1b

¡Jaja, la verdad está fuera! Mi análisis en 2015 fue incorrecto:

El proceso de envío realizó la operación de envío en su contexto, y en este momento la interrupción suave tomó prestado su contexto para activar la operación de recepción, ...

No está "tomando prestado su contexto" en absoluto , ¡pero en realidad es la net_rx_action la que se llama activamente en este contexto!

La lógica de llamada es la siguiente:

ip_output_finish
    rcu_read_lock_bh
    ...
        dev_queue_xmit
            loopback_xmit
                netif_rx
                    enqueue_to_backlog  # 这里将skb入队列
                        raise_softirq_irqoff(NET_RX_SOFTIRQ)
                    ...
                ...
            ...
        ...
    ...
    rcu_read_unlock_bh # unlock操作触发进程上下文中处理接收操作
        local_bh_enable
            do_softirq
                __do_softirq
                    net_rx_action # 这里对队列中的skb进行处理
                        ...
                        ip_rcv_finish
                            icmp_rcv
                        ...
                    ...
                ...
            ...
        ...
    ...
ip_output_finish return

OK, ahora, este es un caso muy claro donde el contexto del proceso ejecuta la lógica de recepción de paquetes, es decir:

  • Dado que la función de interrupción suave net_rx_action puede ejecutarse en el contexto del proceso, para evitar un punto muerto, la sección crítica debe estar protegida por la versión _bh de spinlock.

Similar a rcu_read_unlock_bh, que hace muchas cosas en el proceso de desbloqueo, hay muchas más en el kernel:

  • Spin_unlock puede activar la programación y provocar el cambio de tareas.
  • spin_unlock_bh puede activar do_softirq para ejecutar rutinas de interrupción suave.
  • release_sock puede ejecutar sk_backlog_rcv y luego procesar la colección de paquetes.
  • ...

Este es un efecto de compensación , ya que la operación de bloqueo para prohibir algunos de los comportamientos entre la operación de desbloqueo, entonces tendrían que compensar en la medida de lo posible estos comportamientos retrasados ​​al desbloquear, intente ejecutarlos de inmediato. Este diseño es bastante inteligente.

Además, existe un caso típico en el que el contexto del proceso ejecuta la lógica de recepción del paquete de datos, es decir, la tarjeta de red TUN / TAP llama a tun_get_user desde el contexto del proceso y luego llama directamente a netif_rx_ni para recibir el paquete.

Echemos un vistazo al extraño e interesante proceso de envío y recepción de paquetes de datos mediante la tarjeta de red loopback:

  • La lógica de envío aún no ha regresado y la lógica de recepción regresa primero.

¿Qué significa esto? Desconocido, pero si encuentra algunos problemas inexplicables en el proceso de conexión de esta máquina, puede comenzar desde aquí para solucionarlos.


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/108784871
Recomendado
Clasificación