Todos los productos
Search
Centro de documentos

:Customize RSS for ENIs

Última actualización: Sep 02, 2026

Personalice las reglas de hash y las tablas de redirección en las ENI compatibles para distribuir el tráfico de manera uniforme entre los núcleos de CPU y resolver cuellos de botella de un solo núcleo.

Qué es RSS

Receive Side Scaling (RSS) utiliza el hardware y el controlador de la ENI para calcular un valor de hash a partir de las características del paquete (direcciones IP y puertos de origen/destino), y asigna diferentes flujos de tráfico a múltiples colas RX. Los núcleos de CPU vinculados procesan las colas en paralelo, lo que logra un balanceo de carga y reduce la latencia de un solo núcleo.

An ENI that supports multiple queues utiliza canales independientes de RX (recepción) y TX (envío) por cola para el procesamiento en paralelo. Cuando llega un paquete, el hardware de la ENI usa una directiva de RSS para distribuir diferentes flujos de datos en varias colas RX. De forma predeterminada, la directiva de RSS para una instancia ECS es fija en Virtual Private Cloud (VPC) y no se puede consultar ni modificar desde la instancia.

Algunas familias de instancias admiten la función de RSS personalizado para ENI. Después de enable the custom RSS feature on a supported instance family, la ENI distribuye el tráfico según the default custom hashing rules. También puede adjust the RSS configuration según las características reales del tráfico.

Después de habilitar el RSS personalizado, también puede configure RSS in DPDK scenarios para maximizar el rendimiento multinúcleo.

Cómo funciona

El RSS personalizado para una ENI de instancia ECS funciona de la siguiente manera:

  • Cálculo del valor de hash: La ENI calcula un valor de hash a partir de la 5-tupla (IP de origen, IP de destino, puerto de origen, puerto de destino y protocolo), una clave de hash y un algoritmo de hash.

    • Clave de hash: Un valor fijo de 40 bytes. El controlador de la ENI genera una clave de hash predeterminada durante la inicialización. Puede adjust the hash key para afectar la distribución del tráfico.

    • Algoritmo de hash: Actualmente, solo se admite el algoritmo toeplitz predeterminado. Esta configuración no se puede modificar.

    • Reglas de hash: Las reglas para calcular valores de hash varían según el tipo de tráfico. Las reglas de hash predeterminadas para el RSS de ENI de ECS son las siguientes. No puede cambiar estas reglas.

      • Tráfico TCP y UDP sobre IPv4/IPv6: Se calcula un valor de hash basado en la 4-tupla (IP de origen, IP de destino, puerto de origen y puerto de destino) y la clave de hash.

      • Tráfico no TCP/UDP sobre IPv4/IPv6, como ICMP: Se calcula un valor de hash basado en la 2-tupla (IP de origen e IP de destino) y la clave de hash.

      • Los mensajes que no son IP (como ARP) se envían a la cola predeterminada (cola 0) y no se les aplica hash.

  • Asignación de tabla de redirección: La tabla de redirección de RSS es una matriz predefinida que asigna valores de hash a colas de recepción de destino. Cada elemento (bucket de hash) almacena un número de cola, como 0, 1 o 2. Cuando se carga el controlador de la ENI, este genera automáticamente una tabla de redirección que distribuye uniformemente el tráfico.

    • Longitud de la tabla de redirección: El número total de buckets de hash. El valor es fijo en 128.

    • Modos de distribución:

      El modo de distribución de la tabla de redirección de RSS es el mecanismo principal que asigna valores de hash a colas de recepción específicas. Mediante una tabla de redirección predefinida, el resultado del cálculo de hash se convierte en un índice de cola. Esto permite una distribución flexible del tráfico entre varios núcleos.

      Los diferentes modos de distribución, como la distribución uniforme y la distribución ponderada, afectan directamente la forma en que el tráfico se distribuye entre los núcleos de CPU. Esto, a su vez, afecta el throughput del sistema, la latencia, la utilización de recursos y el rendimiento del servicio.

      Elija un patrón de distribución adecuado según su escenario. Consulte Indirection table distribution pattern.

    • Proceso de asignación de la tabla de redirección:

      1. Cálculo del índice del bucket de hash: Index = Hash value % Indirection table length (Por ejemplo, 88 % 128 → índice 88). Puede usar RSS calculation script para calcular el valor del índice.

      2. Llenado de números de cola: Escriba los números de cola en cada bucket de hash según el modo de distribución.

        Una vez generada la tabla de redirección, el índice de hash de cada paquete se asigna a un número de cola y el paquete se envía a dicha cola.

  • Vinculación de núcleos de CPU: Cuando una cola recibe un paquete, activa una interrupción. El núcleo de CPU vinculado procesa la interrupción.

    Las imágenes distintas de Red Hat Enterprise Linux admiten afinidad de interrupciones de red de forma predeterminada. Cada cola se asocia con una interrupción independiente, y la afinidad de interrupciones distribuye el manejo entre diferentes núcleos de CPU. Consulte Core mechanism of multi-queue.

Habilitar o deshabilitar RSS personalizado para una ENI

Importante

La función de RSS personalizado para ENI se encuentra en vista previa por invitación. Para utilizar esta función, puede submit a ticket.

Después de habilitar el RSS personalizado en un tipo de instancia compatible, el tráfico recibido se distribuye en múltiples colas de recepción según las reglas de hash predeterminadas. Diferentes núcleos de CPU procesan las colas en paralelo, lo que aumenta el throughput y reduce la carga de un solo núcleo.

Requisitos previos

  • El RSS personalizado se está implementando por fases. Está disponible en las siguientes regiones:

    Nombre de región

    ID de región

    China (Hong Kong)

    cn-hongkong

  • Solo algunas familias de instancias de múltiples colas admiten RSS personalizado, incluidas general-purpose instance family g9i y memory-optimized instance family r9i.

    Llame a la operación de API DescribeInstanceTypes para comprobar la compatibilidad. RssSupport=true indica que la función es compatible.

  • Se recomienda la imagen Alibaba Cloud Linux 3.2104 LTS de 64 bits.

    • Si utiliza otras imágenes públicas, la versión del kernel debe ser 6.12 o posterior.

    • Si utiliza la función de RSS personalizado en una aplicación DPDK, la versión de DPDK debe ser 21.11 o posterior.

Cómo habilitar o deshabilitar

El RSS personalizado está deshabilitado de forma predeterminada. Puede habilitarlo o deshabilitarlo al crear una ENI o después de crearla.

  • Al llamar a CreateNetworkInterface, establezca EnableRss en EnhancedNetwork como true o false.

    El RSS personalizado entra en vigor después de que la ENI se adjunta a una instancia.

  • Llame a ModifyNetworkInterfaceAttribute y establezca EnableRss en EnhancedNetwork como true o false. Después de habilitar o deshabilitar, el cambio entra en vigor cuando la ENI se vuelve a adjuntar:

  • Llame a DescribeNetworkInterfaceAttribute con Attribute establecido en enhancedNetwork para consultar si el RSS personalizado está habilitado. EnableRSS=true significa habilitado; false significa deshabilitado.

    Si no se ha modificado el RSS para la ENI, esta operación no devuelve el parámetro EnableRSS, lo que significa que la función no está habilitada.

Consultar la configuración de RSS de una ENI

Después de habilitar el RSS personalizado, el controlador de la ENI genera una configuración de RSS predeterminada based on the default mechanism and rules cuando se recarga la ENI.

Puede remotely log on to a Linux instance y ejecutar ethtool -x eth0 para consultar la configuración de RSS de la ENI principal. Para una ENI secundaria, reemplace el identificador de interfaz (eth1, eth2, etc.).

image

  • Tabla de hash (tabla de redirección de hash de flujo RX): Define las reglas de asignación de valores de hash a las 64 colas de recepción.

    • Cada fila de la tabla de hash representa un rango de valores de hash, indicado por el índice inicial. Por ejemplo, 0 indica valores de hash 0-7, y 8 indica 8-15.

    • Los números en la tabla (0, 1, 2, etc.) representan los ID de las colas de recepción correspondientes. En este ejemplo, hay 64 colas (0-63).

    • Configuración actual: La tabla de redirección se llena cíclicamente con la secuencia 0,1,2,...,63,0,1,2...63, lo que garantiza que los valores de hash se asignen uniformemente a las 64 colas y que el tráfico se distribuya de manera equitativa.

  • Clave de hash RSS: La clave utilizada para calcular el valor de hash. Los caracteres hexadecimales representan una clave de 40 bytes.

  • Función de hash RSS: Define el algoritmo para calcular el valor de hash.

    • toeplitz: Es el algoritmo predeterminado y actualmente está habilitado. Admite hash simétrico basado en una 5-tupla y es adecuado para tráfico de propósito general.

    • xor/crc32: Son otros algoritmos opcionales que suelen utilizarse en escenarios específicos. ECS actualmente solo admite el algoritmo toeplitz, y otros algoritmos no son compatibles ni están habilitados.

  • Reglas de hash: Consulte la configuración de campos de hash para el tráfico recibido por la ENI principal.

    ethtool  -n eth0 rx-flow-hash tcp4
    • Puede reemplazar tcp4 con el tipo de protocolo que desea consultar, como udp4, tcp6 o udp6.

    • Las reglas de hash predeterminadas para el RSS de ENI de ECS son las siguientes. No puede cambiar estas reglas.

      • Tráfico TCP y UDP sobre IPv4/IPv6: Se calcula un valor de hash basado en la 4-tupla (IP de origen, IP de destino, puerto de origen y puerto de destino) y la clave de hash.

      • Tráfico no TCP/UDP sobre IPv4/IPv6, como ICMP: Se calcula un valor de hash basado en la 2-tupla (IP de origen e IP de destino) y la clave de hash.

      • Los mensajes que no son IP (como ARP) se envían a la cola predeterminada (cola 0) y no se les aplica hash.

    • Tomando tcp4 como ejemplo, para el tráfico TCP IPv4, el valor de hash se calcula de forma predeterminada según la 4-tupla (IP de origen, dirección IP de destino, puerto de origen, puerto de destino) y la clave de hash:

      image

      Las características de tráfico esperadas son las siguientes:

      • Misma conexión TCP: Todos los paquetes tienen el mismo valor de hash y se distribuyen a la misma cola. Esto garantiza el orden de los paquetes.

      • Diferentes conexiones TCP: Si las 4-tuplas son diferentes, los valores de hash son diferentes. El tráfico se distribuye a diferentes colas. Esto permite que múltiples núcleos de CPU procesen diferentes conexiones en paralelo y aumenta el throughput.

Si se devuelve el siguiente mensaje, el RSS personalizado no está habilitado o el tipo de instancia no lo admite. Enable custom RSS after confirming that the instance type supports it.

image

Configurar RSS personalizado para una ENI

La configuración de RSS predeterminada satisface la mayoría de los requisitos. Es posible que necesite ajustar la clave de hash o la tabla de redirección en estas situaciones:

  • Tráfico desigual: Los valores de hash predeterminados pueden concentrar tráfico específico en pocas colas.

  • Optimización de rendimiento: Ajuste las reglas de hash según las características del tráfico, como una alta proporción de UDP, para mejorar la utilización de las colas.

  • Requisitos de seguridad: Utilice una clave de hash personalizada para evitar que los atacantes predigan la distribución del tráfico.

Importante
  • En este ejemplo, la instancia está configurada con la imagen Alibaba Cloud Linux 3.2104 LTS 64-bit.

  • Este ejemplo utiliza la ENI principal eth0. Reemplace el identificador de interfaz con eth1 o eth2 para modificar el RSS de la ENI secundaria correspondiente.

  • Cuando la configuración de RSS no coincide con las características de su tráfico, puede causar una distribución desigual y contención de estado entre núcleos, lo que afecta el rendimiento. Ajústela según las características reales del tráfico y utilice monitoring tools para observar la distribución de colas en tiempo real.

Configurar la clave de hash

Regenere la clave de hash de RSS si el tráfico se distribuye de manera desigual (por ejemplo, rx_packets de algunas colas es significativamente mayor que en otras), o para evitar que los atacantes infieran patrones de tráfico mediante ingeniería inversa del valor de hash.

Importante

Modificar la clave cambia los valores de hash de las conexiones existentes, lo que puede causar reordenamiento temporal de paquetes o retransmisiones. Realice esta acción fuera de las horas pico.

  1. Consulte la clave de hash actual de la ENI.

    El controlador de la ENI genera una clave de hash predeterminada de 40 bytes cuando se carga la ENI.

    ethtool -x eth0

    image

  2. Genere una nueva clave aleatoria mediante OpenSSL.

    openssl rand -hex 40 | fold -w2 | paste -sd: -
  3. Aplique la nueva clave a la ENI.

    Importante

    Esta es una configuración temporal. Deja de ser válida después de reiniciar la instancia o volver a adjuntar la ENI. El controlador de la ENI inicializa automáticamente una configuración predeterminada aleatoria.

    ethtool -X eth0 hkey <hash key>

    Reemplace <hash key> con la nueva clave que generó en el paso anterior.

  4. Compruebe que la nueva clave haya surtido efecto.

    ethtool -x eth0

    La salida muestra que la nueva clave ha surtido efecto:

    image

Configurar la tabla de redirección

El modo de distribución de la tabla de redirección de RSS es el mecanismo principal que asigna valores de hash a colas de recepción específicas. Mediante una tabla de redirección predefinida, el resultado del cálculo de hash se convierte en un índice de cola. Esto permite una distribución flexible del tráfico entre varios núcleos.

Los diferentes modos de distribución, como la distribución uniforme y la distribución ponderada, afectan directamente la forma en que el tráfico se distribuye entre los núcleos de CPU. Esto, a su vez, afecta el throughput del sistema, la latencia, la utilización de recursos y el rendimiento del servicio.

Elija un modo de distribución según su escenario.

Importante

Las siguientes configuraciones son temporales. Dejan de ser válidas después de reiniciar la instancia o volver a adjuntar la ENI. El controlador de la ENI reinicializa una configuración predeterminada aleatoria.

  • Modo de distribución uniforme: Las primeras N colas llenan los buckets de hash en un bucle. Adecuado para escenarios generales de alta concurrencia.

    ethtool -X eth0 equal <Number of queues N>

    Si el valor se establece en 64, la tabla de redirección se llena en bucle con 0,1,2,...,63,0,1,2...63,.

    image

  • Distribución ponderada: Asigne buckets de hash según proporciones de peso. Adecuado para escenarios con prioridades de negocio diferenciadas (por ejemplo, la cola 0 procesa tráfico en tiempo real y la cola 1 procesa tareas en segundo plano) o rendimiento mixto de CPU.

    ethtool -X eth0 weight <queue 0 weight> <queue 1 weight> ...

    En el siguiente ejemplo, la cola 0 obtiene el 60% y la cola 1 obtiene el 40%:

    Nota

    Si hay cuatro colas en total, pero solo establece pesos para dos colas, la tabla de redirección solo generará asignaciones para la cola 0 y la cola 1.

    ethtool -X eth0 weight 6 4

    image

  • Distribución parcial de colas: A partir de una cola especificada, utilice colas consecutivas para llenar buckets de hash en un bucle. Adecuado para dirigir tráfico a un rango específico de CPU, como un nodo NUMA.

    ethtool -X eth0 start <start queue> equal <number of queues>

    En el siguiente ejemplo, las colas 2-41 (40 colas a partir de la cola 2) llenan los buckets de hash:

    ethtool -X eth0 start 2 equal 40

    image

Uso de watch para observar la distribución del tráfico

Después de habilitar el RSS personalizado en la ENI, utilice hping3 para generar tráfico con diferentes características y watch para monitorear la distribución de interrupciones de cola, verificando si el tráfico se distribuye a múltiples núcleos como se espera.

Requisitos previos

Adquiera dos instancias ECS con estas configuraciones:

  • Instancia ECS emisora (10.0.0.252): Una instancia con hping3 instalado para generar tráfico con diferentes características.

  • Instancia ECS receptora: Una instancia cuyo tipo admite multi-queue for ENIs, con a secondary ENI attached (10.0.0.5) y custom RSS enabled on the secondary ENI eth1.

    Nota
    • Este ejemplo utiliza cuatro colas para las pruebas.

    • Para eliminar el impacto del tráfico de inicio de sesión SSH en la ENI principal, se habilita el RSS personalizado en la ENI secundaria eth1 para las pruebas.

  • Conectividad de red: Las dos instancias ECS están en el mismo security group y pueden comunicarse entre sí a través de la red interna.

Procedimiento

  1. Inicie sesión en la instancia ECS receptora, compruebe la configuración de RSS de la ENI secundaria y confirme la distribución de tráfico esperada. Consulte View the RSS configuration of an ENI.

    image

  2. Inicie sesión en la instancia ECS emisora e instale hping3.

    yum install -y hping3
  3. En la instancia ECS receptora, monitoree los conteos de paquetes de cola en tiempo real.

    watch -n 1 "ethtool -S eth1 | grep rx[0,1,2,3]_packets"

    Reemplace la configuración del número de cola con la configuración real de la ENI receptora.

  4. Inicie sesión en la instancia ECS emisora y simule diferentes patrones de tráfico.

    • Escenario 1: Envíe 10,000 paquetes SYN a alta velocidad a la dirección IP receptora con puertos de destino aleatorios. Esto asegura que el hash de tráfico se disperse y verifica el balanceo de hash.

      sudo hping3 10.0.0.5 -S -a 10.0.0.252 --rand-dest -p 0 --baseport 10000 -c 10000 -i u100 -I eth0
      • rand-dest: Puerto de destino aleatorio

      • -p 0: Se usa con rand-dest

      • --baseport 10000: El valor inicial para el puerto de origen.

      • -c 10000: Enviar 10,000 paquetes

      • -i u100: Especifica un intervalo de 100 microsegundos entre paquetes para envío a alta velocidad.

      • -I eth0: Se usa con rand-dest para especificar la interfaz de red del emisor

      En la instancia receptora, los conteos de paquetes de las cuatro colas deberían aumentar uniformemente:

      image

    • Escenario 2: Envíe tráfico con una dirección IP de origen y un puerto fijos. Esto prueba si el mismo tráfico se asigna a la misma cola y verifica la consistencia del hash.

      El siguiente comando envía 10,000 paquetes TCP SYN al objetivo 10.0.0.5:80, con la IP de origen fija en 10.0.0.252 y el puerto de origen fijo en 12345.

      sudo hping3 10.0.0.5 -S -p 80 -c 10000 -s 12345 -a 10.0.0.252 --keep -i u100
      • El valor del índice de hash se calcula según RSS script. Se espera que el tráfico sea procesado por la cola 0:

        image

      • Resultado de observación real en la instancia ECS receptora (10.0.0.5):

        image

  5. Si la distribución del tráfico no cumple con las expectativas (por ejemplo, una cola tiene una carga significativamente mayor), optimice mediante adjusting the hash key o configuring the indirection table.

Configurar y usar RSS en DPDK

Data Plane Development Kit (DPDK) es un framework de aceleración de plano de datos en modo usuario de código abierto que utiliza controladores en modo usuario, copia cero y modo de sondeo para lograr un procesamiento de paquetes cercano a la velocidad de línea. Es adecuado para dominios sensibles al throughput y la latencia, como nubes de telecomunicaciones, tecnología financiera y computación perimetral. Consulte Data Plane Development Kit (DPDK*).

Después de habilitar el RSS personalizado en la ENI, puede usar testpmd y l3fwd en DPDK para probar y verificar la distribución de RSS.

Instalar y configurar DPDK en una instancia ECS

Nota
  • Si utiliza la función de RSS personalizado en una aplicación DPDK, la versión de DPDK debe ser 21.11 o posterior.

  • Este tema utiliza una instancia ecs.r9i.16xlarge (64 colas) que ejecuta la imagen Alibaba Cloud Linux 3.2104 LTS 64-bit como ejemplo para demostrar la instalación de la versión 22.11.3 de DPDK.

  • Este ejemplo utiliza la ENI principal eth0. Reemplace el identificador de interfaz con eth1 o eth2 para modificar el RSS de la ENI secundaria correspondiente.

Paso 1: Instalar DPDK

Haga clic para ver los pasos de ejemplo para instalar DPDK

  1. Actualice el sistema e instale herramientas básicas.

    sudo yum update -y
    sudo yum install -y git wget gcc make kernel-devel-$(uname -r) numactl-devel python3 pciutils
  2. Instale las dependencias de DPDK.

    sudo yum install -y libpcap-devel meson ninja-build
  3. Configure páginas enormes (huge pages).

    • DPDK omite la pila de protocolos del kernel para operar directamente la ENI. Utiliza páginas enormes (normalmente de 2 MB) en lugar de páginas de 4 KB para reducir los fallos de TLB y mejorar la velocidad de acceso a la memoria.

    • Asignar demasiadas páginas enormes reduce la memoria normal disponible para el sistema operativo. Esto puede provocar fallos en otras aplicaciones o servicios del sistema. Una asignación excesiva también puede impedir la conectividad de la instancia.

    • Calcule el número requerido de páginas enormes según las necesidades de memoria de su aplicación.

      Número de páginas enormes = Memoria de la aplicación / Tamaño de página. El tamaño de página predeterminado es de 2 MB. Por ejemplo, 16 GB / 2 MB = 8192 páginas.

    echo "vm.nr_hugepages = 8192" | sudo tee -a /etc/sysctl.conf
    sudo sysctl -p
  4. Cree y monte el directorio de páginas enormes.

    sudo mkdir -p /dev/hugepages
    sudo mount -t hugetlbfs hugetlbfs /dev/hugepages
  5. Descargue el código fuente de DPDK. Se requiere conexión a Internet.

    cd ~
    wget https://fast.dpdk.org/rel/dpdk-22.11.3.tar.xz
    tar xf dpdk-22.11.3.tar.xz
    cd dpdk-stable-22.11.3
  6. Compile DPDK.

    # Initialize the build directory and configure project options, specifying to build the l3fwd Layer 3 forwarding example.
    meson setup -Dexamples=l3fwd build
    cd build
    # Compile.
    ninja
    # Install the compiled files to the system directory.
    sudo ninja install
    # Update the system's shared library cache.
    sudo ldconfig

    Si encuentra el error missing python module: elftools que se muestra en la figura siguiente durante la compilación,

    image

    Especifique su versión de Python, instale pyelftools y vuelva a compilar:

    sudo /usr/bin/python3.8 -m pip install pyelftools

Paso 2: Cargar módulos del kernel

DPDK requiere módulos del kernel como UIO o VFIO para el acceso a dispositivos en modo usuario. Se prefiere VFIO por su seguridad (se basa en IOMMU), mientras que UIO es adecuado para pruebas rápidas. Este ejemplo utiliza VFIO.

  1. Habilite IOMMU.

    VFIO depende de IOMMU para la vinculación segura de dispositivos en modo usuario y la asignación de DMA.

    1. Abra el archivo.

      sudo vim /etc/default/grub
    2. Presione i para entrar al modo de inserción. Agregue intel_iommu=on al parámetro GRUB_CMDLINE_LINUX. Guarde y cierre el archivo.

      Ejemplo de la configuración modificada:

      GRUB_TIMEOUT=1
      GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
      GRUB_DEFAULT=saved
      GRUB_DISABLE_SUBMENU=true
      GRUB_TERMINAL_OUTPUT="console"
      GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet idle=halt biosdevname=0 net.ifnames=0 console=tty0 console=ttyS0,115200n8 noibrs intel_iommu=on"
      GRUB_DISABLE_RECOVERY="true"
    3. Aplique la configuración.

      sudo grub2-mkconfig -o /boot/grub2/grub.cfg
      [test@iZ2ze9h6rn          ~]$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
      Generating grub configuration file ...
      Found linux image: /boot/vmlinuz-4.19.91-27.4.al7.x86_64
      Found initrd image: /boot/initramfs-4.19.91-27.4.al7.x86_64.img
      Found linux image: /boot/vmlinuz-0-rescue-2cea6e3dd20d4251bf6880159559f2c9
      Found initrd image: /boot/initramfs-0-rescue-2cea6e3dd20d4251bf6880159559f2c9.img
      done
    4. Reinicie la instancia y vuelva a conectarse después de que se inicie.

      reboot
      Aviso

      La operación de reinicio detiene la instancia durante un breve período de tiempo y puede interrumpir los servicios que se ejecutan en ella. Le recomendamos reiniciar la instancia fuera de las horas pico.

  2. Instale los controladores VFIO y VFIO-PCI.

    sudo modprobe vfio && \
    sudo modprobe vfio-pci
  3. Habilite noiommu_mode.

    sudo bash -c 'echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode'

Paso 3: Vincular la ENI al controlador DPDK

  1. Enable custom RSS on the ENI.

    Asegúrese de que el RSS personalizado esté habilitado para la ENI que tomará DPDK y de que la ENI se haya vuelto a conectar para que el cambio surta efecto.

  2. Connect to an instance using VNC.

    • Este ejemplo utiliza la ENI principal. Las operaciones posteriores desconectan la ENI, lo que interrumpe las sesiones SSH.

    • Para operaciones con ENI secundarias, utilice connect using Workbench (primary ENI) en su lugar.

  3. Consulte el estado de vinculación del controlador del dispositivo PCI. De forma predeterminada, el kernel gestiona la ENI.

    dpdk-devbind.py --status

    image

    • El dispositivo 0000:00:05.0 es gestionado por el controlador del kernel virtio-pci y eth0 está activa.

    • El dispositivo puede cambiarse a vfio-pci para que DPDK lo tome en modo usuario. Primero deshabilite la interfaz y desvincule el controlador del kernel.

  4. Desactive eth0.

    sudo ip link set dev eth0 down

    Si omite este paso, la vinculación a VFIO falla:

    image

  5. Desvincule el controlador del kernel y vincúlelo a VFIO.

    dpdk-devbind.py -b vfio-pci 0000:00:05.0

    Reemplace el identificador del dispositivo PCI por el que consultó para su ENI.

    Nota

    Para volver a vincular el controlador del kernel, detenga la aplicación DPDK con sudo pkill dpdk-app y ejecute dpdk-devbind.py -b virtio-pci 0000:00:05.0.

  6. Consulte nuevamente el estado de vinculación del controlador del dispositivo PCI. La ENI ahora debería estar bajo el control de DPDK.

    Importante

    Cuando el controlador de modo usuario de DPDK toma la ENI, el kernel deja de controlarla. Comandos como ip a no pueden mostrar su información.

    dpdk-devbind.py --status

    image

    El dispositivo 0000:00:05.0 ahora está vinculado a vfio-pci.

Configurar RSS mediante testpmd

Testpmd es una herramienta de pruebas de DPDK para verificar las funciones del controlador ENI y depurar aplicaciones del plano de datos. Consulte Funciones en tiempo de ejecución de Testpmd.

Importante

Las modificaciones en la configuración de la ENI (cantidad de colas, reglas RSS, tabla RETA) solo surten efecto después de iniciar o reiniciar el reenvío de paquetes.

  1. Connect to an instance using VNC.

  2. Inicie la herramienta de prueba de reenvío de paquetes testpmd de DPDK.

    dpdk-testpmd -a 0000:00:05.0 --socket-mem 1024 -- -i --portmask=0x1 --rxq=64 --txq=64  --forward-mode=rxonly
    • -a 0000:00:05.0: Conecta la ENI en la dirección PCI 0000:00:05.0 a DPDK. Ejecute dpdk-devbind.py --status para obtener la dirección PCI.

    • --socket-mem 1024: Preasigna 1024 MB de memoria de páginas grandes por nodo NUMA.

    • -i: Inicia el modo interactivo de línea de comandos para ajustar dinámicamente la configuración y consultar estadísticas. Introduzca quit para salir.

    • --portmask=0x1: Habilita la máscara de puerto 0x1 (binario 0001), y utiliza únicamente la primera ENI (dirección PCI 0000:00:05.0).

      • Cada bit binario de portmask corresponde a un puerto (0x1 habilita el puerto 0, 0x3 habilita los puertos 0 y 1).

      • Solo hay un dispositivo conectado (0000:00:05.0), por lo que su número de puerto DPDK es 0.

    • --rxq=64 / --txq=64: Establece la cantidad de colas RX y TX por puerto en 64 para el procesamiento multicola. Reemplace 64 con la ENI queue count real.

    • --forward-mode=rxonly: Modo de solo recepción (los paquetes se reciben y se descartan). Se usa para probar el rendimiento de recepción de la ENI o para capturar paquetes.

  3. Ingrese al modo interactivo y consulte la configuración actual de RSS.

    • Consultar información de configuración de hash: show port info <port_id>

      port_id: El puerto. En este ejemplo, el puerto 0.

      Haga clic para ver un ejemplo de salida

      image

    • Consultar clave de hash: show port <port_id> rss-hash key

      image

    • Consultar configuración de tabla de indirección: show port <port_id> rss reta <size> <mask0, mask1...>

      • size: La cantidad de entradas de la tabla de indirección que se van a consultar. El valor es fijo: 128.

      • mask0, mask1: Las máscaras utilizadas para filtrar el rango de índices de hash que se mostrarán, especificadas en formato hexadecimal. Por ejemplo, mask0=0xff indica que se muestran las entradas correspondientes a los índices de hash 0 a 7.

        El tamaño de la tabla de indirección es fijo: 128. Se requieren dos máscaras, cada una cubre 64 bloques de índices. Ejecute show port 0 rss reta 128 (0xffffffffffffffff,0xffffffffffffffff) para obtener las 128 asignaciones de índice a cola:

        Haga clic para ver un ejemplo de salida

        image

  4. Configure RSS según sus necesidades.

    • Configure una nueva clave de hash.

      Generate a new random key with OpenSSL.

      port config <port_id> rss-hash-key (ipv4|ipv4-frag|\
                        ipv4-tcp|ipv4-udp|ipv4-sctp|ipv4-other|\
                        ipv6|ipv6-frag|ipv6-tcp|ipv6-udp|ipv6-sctp|\
                        ipv6-other|l2-payload|ipv6-ex|ipv6-tcp-ex|\
                        ipv6-udp-ex <string of hex digits \
                        (variable length, NIC dependent)>)

      Para TCP sobre IPv4, ejecute port config 0 rss-hash-key ipv4 6D5A56DA255B0EC24167253D43A38FB0D0CA2BCBAE7B30B477CB2DA38030F20C6A42B73BBEAC01FC para configurar la clave de hash y verifíquela:

      image

    • Configure la tabla de indirección RSS para asignar valores de hash a colas específicas.

      port config all rss reta <hash,queue>,<hash,queue>..

      Realice la configuración según la cantidad real de colas:

      • hash: El índice de hash. El rango depende del tamaño de la tabla de indirección (por ejemplo, 0-63 para 64 entradas).

      • queue: El número de la cola de recepción destino.

Aplicar RSS en l3fwd

Para habilitar RSS en una aplicación DPDK, implemente la configuración de la clave de hash y de la tabla de indirección en su código. A continuación se utiliza L3FWD como ejemplo.

L3FWD (Layer 3 Forwarding) es una aplicación de ejemplo de DPDK que demuestra el enrutamiento de paquetes basado en IP de alto rendimiento mediante copia cero y controladores en modo de sondeo (PMD).

  1. Modifique el código fuente de L3FWD en DPDK (examples/l3fwd/main.c).

    • Modifique la sección de inicialización de puertos static struct rte_eth_conf port_conf en el código de ejemplo de L3FWD.

      Haga clic para ver la modificación del código

      #define RSS_HASH_KEY_LENGTH 	40
      #define RSS_RETA_SIZE 			128
      
      static uint8_t hash_key[RSS_HASH_KEY_LENGTH] = {
          0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
          0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
          0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
          0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A,
      };
      
      static struct rte_eth_conf port_conf = {
      	.rxmode = {
      		.mq_mode = RTE_ETH_MQ_RX_RSS,
      		.offloads = RTE_ETH_RX_OFFLOAD_UDP_CKSUM | RTE_ETH_RX_OFFLOAD_TCP_CKSUM,
      	},
      	.rx_adv_conf = {
      		.rss_conf = {
      			.rss_key = hash_key,
      			.rss_hf = RTE_ETH_RSS_IP,
      			.rss_key_len = RSS_HASH_KEY_LENGTH,
      		},
      	},
      	.txmode = {
      		.mq_mode = RTE_ETH_MQ_TX_NONE,
      	},
      };
    • Agregue funciones para configurar la tabla de indirección de hash y la clave de hash.

      Haga clic para ver el código agregado

      /**
       * @brief Configure the RSS RETA (Redirection Table Array) for a given port.
       * 
       * @param port_id The ID of the port to configure.
       */
      
      void configure_rss_reta(uint16_t port_id) {
          struct rte_eth_rss_reta_entry64 reta_conf[RSS_RETA_SIZE / RTE_ETH_RETA_GROUP_SIZE];
          unsigned int i;
          uint16_t reta_size;
      	uint16_t nb_queues;
      	struct rte_eth_dev_info dev_info;
      
      	if (port_id >= RTE_MAX_ETHPORTS) {
              printf("port_id %d exceed max eth ports\n", port_id);
              return;
          }
      
          if (rte_eth_dev_info_get(port_id, &dev_info) != 0) {
      		printf("Failed to get device info for port %d\n", port_id);
      		return;
      	}
      
          reta_size = dev_info.reta_size;
          if (reta_size == 0) {
              printf("Device does not support RSS RETA configuration.\n");
              return;
          }
      
      	nb_queues = dev_info.nb_rx_queues;
      	if (nb_queues == 0) {
      		printf("port %d RX queues = 0\n", port_id);
      		return;
      	}
      
          // Initialize RETA table
          memset(reta_conf, 0, sizeof(reta_conf));
          for (i = 0; i < reta_size; i++) {
              reta_conf[i / RTE_ETH_RETA_GROUP_SIZE].reta[i % RTE_ETH_RETA_GROUP_SIZE] = (uint16_t)(i % nb_queues);
          }
      
          // Configure RETA table mask
          for (i = 0; i < reta_size; i += RTE_ETH_RETA_GROUP_SIZE) {
              reta_conf[i / RTE_ETH_RETA_GROUP_SIZE].mask = UINT64_MAX;
          }
      
      	// Update RSS RETA table to device
      	if (rte_eth_dev_rss_reta_update(port_id, reta_conf, reta_size) != 0) {
      		printf("Failed to update RSS RETA table for port %u\n", port_id);
      		return;
      	}
      }
      
      /**
       * @brief Configure the RSS hash key for a given port.
       * 
       * @param port_id The ID of the port to configure.
       */
      void configure_rss_hash_key(uint16_t port_id) {
          struct rte_eth_rss_conf rss_conf;
      
      	if (port_id >= RTE_MAX_ETHPORTS) {
              printf("port_id %d exceed max eth ports\n", port_id);
              return;
          }
      
          memset(&rss_conf, 0, sizeof(rss_conf));
          rss_conf.rss_key = hash_key;
          rss_conf.rss_key_len = sizeof(hash_key);
          
          // Update RSS hash key to device
          if (rte_eth_dev_rss_hash_update(port_id, &rss_conf) != 0) {
              printf("Failed to update RSS hash key for port %u\n", port_id);
      		return;
          }
      }
    • Después de rte_eth_dev_start, llame a las nuevas funciones de configuración.

      Haga clic para ver el código de llamada en main

      @@ -1472,12 +1576,15 @@ main(int argc, char **argv)
                      /* Start device */
      		ret = rte_eth_dev_start(portid);
      		if (ret < 0)
      			rte_exit(EXIT_FAILURE,
      				"rte_eth_dev_start: err=%d, port=%d\n",
      				ret, portid);
      				
      		configure_rss_reta(portid);
      		configure_rss_hash_key(portid);
  2. Vuelva a compilar L3FWD después de modificar el código fuente.

    cd ~/dpdk-stable-22.11.3/
    rm -rf build
    # Initialize the build directory and configure project options, specifying to build the l3fwd Layer 3 forwarding example.
    meson setup -Dexamples=l3fwd build
    cd build
    # Compile.
    ninja
    # Install the compiled files to the system directory.
    sudo ninja install
    # Update the system's shared library cache.
    sudo ldconfig
  3. Especifique las vinculaciones puerto-cola-núcleo e inicie L3FWD.

    cd ~/dpdk-stable-22.11.3/build/examples
    ./dpdk-l3fwd --legacy-mem -a 0000:00:05.0 --socket-mem 1024 -- -p 0x1 --config="(PORT_ID, QUEUE_ID, LCORE_ID), (PORT_ID, QUEUE_ID, LCORE_ID), ..." --parse-ptype
    • --config: Cada tríada especifica:

      • PORT_ID: El ID del puerto (comienza en 0).

      • QUEUE_ID: El ID de la cola de recepción (comienza en 0).

      • LCORE_ID: El ID del núcleo lógico (comienza en 0).

    • El siguiente ejemplo utiliza dos núcleos y dos colas:

      ./dpdk-l3fwd --legacy-mem -a 0000:00:05.0 --socket-mem 1024 -- -p 0x1 --config="(0,0,0),(0,1,1)"  --parse-ptype
      • La primera tríada (0,0,0): El núcleo lógico 0 (lcore0) procesa la cola 0 del puerto 0.

      • La segunda tríada (0,1,1): El núcleo lógico 1 (lcore1) procesa la cola 1 del puerto 0.

      image

Usar el script RSS para calcular un índice de hash

Utilice este script de Python para calcular a qué cola de recepción se asigna un paquete, dada su 4-tupla y su clave de hash. El resultado sirve como guía para configurar la tabla de indirección y dirigir flujos específicos a colas designadas.

Haga clic para ver el contenido del script** ali_ecs_rss_calc.py

#!/usr/bin/python

import sys
import argparse
import re
import ipaddress

prog_name = sys.argv[0]
USAGE_EXAMPLE = """
Usage example:
    Calculate the Toeplitz hash of a packet sent from 1.2.3.4 to 1.2.3.5 with a source and destination
    port of 7000:

    - The hash key argument is required because the virtio-net driver creates a random hash key for all your NICs.

    $ {prog_name} -t 1.2.3.4 -T 7000 -r 1.2.3.5 -R 7000 -k 77:d1:c9:34:a4:c9:bd:87:6e:35:dd:17:b2:e3:23:9e:39:6d:8a:93:2a:95:b4:72:3a:b3:7f:56:8e:de:b6:01:97:af:3b:2f:3a:70:e7:04
    
    - If you want to calculate the RSS value of a packet whose protocol is not TCP or UDP, do not specify
      the source and destination ports. The script will calculate the hash value based on only the IP addresses.
      
    $ {prog_name} -t 1.2.3.4 -r 1.2.3.5 -k 77:d1:c9:34:a4:c9:bd:87:6e:35:dd:17:b2:e3:23:9e:39:6d:8a:93:2a:95:b4:72:3a:b3:7f:56:8e:de:b6:01:97:af:3b:2f:3a:70:e7:04
    
    - Use "--ipv6" to calculate the RSS value for IPv6 packets.
    
    $ {prog_name} -t 2001:250:250:250:250:250:250:1 -T 7000 -r 2001:250:250:250:250:250:250:2 -R 7000 -k 77:d1:c9:34:a4:c9:bd:87:6e:35:dd:17:b2:e3:23:9e:39:6d:8a:93:2a:95:b4:72:3a:b3:7f:56:8e:de:b6:01:97:af:3b:2f:3a:70:e7:04 --ipv6

    Note: Linux kernel 5.9 or later is required for hash function and key configuration support.
    
    Also, Linux kernels older than ANCK 5.10-018 had a bug where the default hash key shown by ethtool was not 
    the same as the one used by the device before any RSS configuration was manually changed. The script will print the
    correct hash value in this case if you do not specify the hash key during calculation.
""".format(prog_name = prog_name)

# The default key on instances for old alinux kernel(< ANCK 5.10-018) should be as below, not the one you get from "ethtool -x <nic_name>".
RSS_DEFAULT_KEY = [
	0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2,
	0x41, 0x67, 0x25, 0x3D, 0x43, 0xA3, 0x8F, 0xB0,
	0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4,
	0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C,
	0x6A, 0x42, 0xB7, 0x3B, 0xBE, 0xAC, 0x01, 0xFA,
]
# The default key on instances for new alinux kernel(>= ANCK 5.10-018) is randomly generated.

TOEPLITZ_KEY_SIZE = 128
BITS_IN_BYTE = 8

def circular_shift_key_one_left(key):
    """Performs a cyclic left shift of the entire key.
    To cyclically shift all 40 bytes, the function
    shifts bits between adjacent bytes one at a time."""

    l = len(key)
    return [ ((key[i] << 1) & 0xff) | ((key[(i + 1) % l] & 0x80) >> 7) for i in range(0, l) ]

def or_32msb_bits_of_key(key):
    return (key[0] << 24) | (key[1] << 16) | (key[2] << 8) | key[3]

def calculate_hash(rx_ip, rx_port, tx_ip, tx_port, initial_value, key):
    """Calculates the Toeplitz hash based on the provided parameters.
    Note: This implementation is specific to ENA and may not be
    compatible with the standard Toeplitz implementation."""

    hash_result = initial_value
    input_bytes = list()
    input_bytes += tx_ip + rx_ip + tx_port + rx_port

    for input_byte in input_bytes:
        for i in range(BITS_IN_BYTE):
            # is the (8 - i -1) bit set
            if (input_byte & (1 << (BITS_IN_BYTE - i - 1))):
                hash_result ^= or_32msb_bits_of_key(key)

            key = circular_shift_key_one_left(key)

    return hash_result

def ipv4_addr_type(str):
    """An argparse type function that transforms an
    IPv4 string into a list of integers."""
    if not re.match(r"^([0-9]{1,3}\.){3}[0-9]{1,3}$", str):
        raise argparse.ArgumentTypeError("The IP address must be in the format 1.2.3.4.")

    return [int(octet) for octet in str.split('.')]

def ipv6_addr_type(str):
    """An argparse type function that transforms an
    IPv6 string into a list of integers."""
    
    try:
        ip_str = ipaddress.IPv6Address(unicode(str))
    except ValueError as e:
        raise argparse.ArgumentTypeError("Invalid IPv6 address format: %s" % e)
    
    parts = ip_str.exploded.split(':')
    try:
        bytes_list = [int(part, 16) for part in parts]
        bytes_list = [(byte >> 8, byte & 0xff) for byte in bytes_list]
    except ValueError as e:
        raise argparse.ArgumentTypeError("Invalid IPv6 address format: %s" % e)
    
    return [item for sublist in bytes_list for item in sublist]

def toeplitz_key_type(str):
    """An argparse type function that transforms a
    Toeplitz key string into a list of hexadecimal values."""
    if not re.match(r"^([0-9a-zA-Z]{1,2}:){39}[0-9a-zA-Z]{1,2}$", str):
        raise argparse.ArgumentTypeError("The Toeplitz key format is invalid. It must be 40 hexadecimal values delimited by colons.")

    return [int(key_elem, 16) for key_elem in str.split(':')]

def main():

    parser = argparse.ArgumentParser(description='virtio-net Toeplitz hash calculator',
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     epilog=USAGE_EXAMPLE)

    parser.add_argument('-r', '--rx-ip', help='Receiving side IP', dest='rx_ip', nargs='?',
                        required=True, type=str)
    parser.add_argument('-R', '--rx-port', help='Receiving side port', dest='rx_port', nargs='?', type=int)
    parser.add_argument('-t', '--tx-ip', help='Transmitting side IP', dest='tx_ip', nargs='?', 
                        required=True, type=str)
    parser.add_argument('-T', '--tx-port', help='Transmitting side port', dest='tx_port', nargs='?', type=int)
    parser.add_argument('-k', '--toeplitz-key',
                        help='The Toeplitz key (only on instances that support changing it)',
                        dest='toeplitz_key', nargs='?', required=True, type=toeplitz_key_type)
    parser.add_argument('-i', '--ipv6',  action='store_true', help='Use IPv6 address type for IP arguments')

    args = parser.parse_args()

    if args.ipv6:
        rx_ip   = ipv6_addr_type(args.rx_ip)
        tx_ip   = ipv6_addr_type(args.tx_ip)
    else:
        rx_ip   = ipv4_addr_type(args.rx_ip)
        tx_ip   = ipv4_addr_type(args.tx_ip)
    
    if args.rx_port and args.tx_port:
        # "break" port number into two byte representation
        rx_port = [(args.rx_port & 0xff00) >> 8, args.rx_port & 0x00ff]
        tx_port = [(args.tx_port & 0xff00) >> 8, args.tx_port & 0x00ff]
    else:
        rx_port = tx_port = []
    
    key = args.toeplitz_key

    # calculate the hash with an initial value of 0
    hash = calculate_hash(rx_ip, rx_port, tx_ip, tx_port, 0, key)
    rss_table_entry_128 = hash % 128
    rss_table_entry_256 = hash % 256

    if args.ipv6:
        print("Sending traffic from [{}]:{} to [{}]:{}".format(args.tx_ip, args.tx_port, args.rx_ip, args.rx_port))
    else:
        print("Sending traffic from {}:{} to {}:{}".format(args.tx_ip, args.tx_port, args.rx_ip, args.rx_port))
    print("""The hash is calculated over the following fields:
    Source IP address
    Destination IP address""")
    if args.rx_port and args.tx_port:
        print("""    Source port
    Destination port""")
    print("Should result in the hash for all drivers:".ljust(50) + "{}".format(hex(hash)))
    print("RSS table entry (total length 128):".ljust(50) + "{}".format(rss_table_entry_128))
    print("RSS table entry (total length 256):".ljust(50) + "{}".format(rss_table_entry_256))
    return

if __name__ == '__main__':
    main()

Haga clic para ver el uso del script

Consulte el uso del script: python ali_ecs_rss_calc.py -h.

image

El siguiente ejemplo utiliza la configuración RSS de una ENI con 64 colas:

image

Calcule el valor de hash RSS mediante el script con la siguiente información de 5-tupla (modifique los valores según sea necesario).

  • Dirección IP de destino (-r): 10.0.0.1, que es la dirección IP de la ENI de la instancia receptora con RSS configurado.

  • Dirección IP de origen (-t): 10.0.0.251, que es la dirección IP de la ENI del remitente.

  • Puerto de destino (-R): 26000

  • Puerto de origen (-T): 18042

  • Clave de hash: Obténgala de la configuración de la tabla de indirección RSS.

python ali_ecs_rss_calc.py -r 10.0.0.1 -t 10.0.0.251 -R 26000 -T 18042 -k 69:e8:7c:56:bf:03:9f:63:d7:c5:e5:96:b3:00:36:93:02:8c:d2:8f:cc:a9:00:65:fd:c8:94:71:5f:fd:c8:de:7a:30:a9:73:b3:33:0c:c6

El script calcula el hash mediante el algoritmo toeplitz. En el resultado siguiente, el sistema utiliza una longitud de tabla de indirección de 128. Un índice de hash de 117 significa que el paquete se asigna a la cola en el índice 117.

image

Consulte la tabla de indirección: el índice 117 se asigna a la cola 53, por lo que los paquetes son procesados por la cola 53.

image