Todos os produtos
Search
Central de documentação

:Customize RSS for ENIs

Última atualização: Jul 03, 2026

Personalize as regras de hash RSS e as tabelas de indireção em ENIs compatíveis para distribuir o tráfego uniformemente entre os núcleos de CPU e resolver gargalos de núcleo único.

O que é RSS

O Receive Side Scaling (RSS) utiliza o hardware e o driver da ENI para calcular um valor de hash com base nas características dos pacotes (endereços IP e portas de origem/destino), mapeando diferentes fluxos de tráfego para múltiplas filas RX. Os núcleos de CPU vinculados processam essas filas em paralelo, garantindo o balanceamento de carga e reduzindo a latência em núcleos individuais.

Uma ENI com suporte a múltiplas filas usa canais independentes de RX (recebimento) e TX (envio) por fila para processamento paralelo. Quando um pacote chega, o hardware da ENI aplica uma política de RSS para distribuir os diferentes fluxos de dados entre várias filas RX. Por padrão, a política de RSS de uma instância ECS é fixa na Virtual Private Cloud (VPC) e não pode ser visualizada ou modificada de dentro da instância.

Algumas famílias de instâncias oferecem suporte ao recurso de RSS personalizado para ENIs. Após você ativar o recurso de RSS personalizado em uma família de instâncias compatível, a ENI distribui o tráfego com base nas regras de hash personalizadas padrão. Também é possível ajustar a configuração de RSS conforme as características reais do tráfego.

Após ativar o RSS personalizado, você também pode configurar o RSS em cenários DPDK para maximizar o desempenho multinúcleo.

Como funciona

O RSS personalizado para uma ENI de instância ECS funciona da seguinte maneira:

  • Cálculo do valor de hash: A ENI calcula um valor de hash a partir da tupla-5 (IP de origem, IP de destino, porta de origem, porta de destino e protocolo), uma chave de hash e um algoritmo de hash.

    • Chave de hash: Um valor fixo de 40 bytes. O driver da ENI gera uma chave de hash padrão durante a inicialização. Você pode ajustar a chave de hash para influenciar a distribuição do tráfego.

    • Algoritmo de hash: Atualmente, há suporte apenas para o algoritmo padrão toeplitz. Essa configuração não pode ser modificada.

    • Regras de hash: As regras para cálculo dos valores de hash variam conforme o tipo de tráfego. As regras de hash padrão para RSS de ENI ECS são listadas abaixo e não podem ser alteradas.

      • Tráfego TCP e UDP sobre IPv4/IPv6: O valor de hash é calculado com base na tupla-4 (IP de origem, IP de destino, porta de origem e porta de destino) e na chave de hash.

      • Tráfego não TCP/UDP sobre IPv4/IPv6, como ICMP: O valor de hash é calculado com base na tupla-2 (IP de origem e IP de destino) e na chave de hash.

      • Mensagens sem IP (como ARP) são enviadas para a fila padrão (fila 0) e não passam pelo processo de hash.

  • Mapeamento da tabela de indireção: A tabela de indireção RSS é um array predefinido que mapeia valores de hash para filas de recebimento de destino. Cada elemento (bucket de hash) armazena um número de fila, como 0, 1 ou 2. Ao carregar o driver da ENI, ele gera automaticamente uma tabela de indireção que distribui uniformemente o tráfego.

    • Comprimento da tabela de indireção: O número total de buckets de hash. O valor é fixo em 128.

    • Modos de distribuição:

      O modo de distribuição da tabela de indireção RSS é o mecanismo central que mapeia valores de hash para filas de recebimento específicas. Utilizando uma tabela de indireção predefinida, o resultado do cálculo de hash é convertido em um índice de fila, permitindo uma distribuição flexível do tráfego entre múltiplos núcleos.

      Diferentes modos de distribuição, como distribuição uniforme e ponderada, afetam diretamente a forma como o tráfego é distribuído entre os núcleos de CPU. Isso impacta o throughput do sistema, a latência, a utilização de recursos e o desempenho do serviço.

      Escolha um padrão de distribuição adequado ao seu cenário. Consulte Padrão de distribuição da tabela de indireção.

    • Processo de mapeamento da tabela de indireção:

      1. Cálculo do índice do bucket de hash: Index = Hash value % Indirection table length (Por exemplo, 88 % 128 → índice 88). Use o script de cálculo RSS para obter o valor do índice.

      2. Preenchimento dos números de fila: Escreva os números das filas em cada bucket de hash conforme o modo de distribuição.

        Após a geração da tabela de indireção, o índice de hash de cada pacote é mapeado para um número de fila, e o pacote é enviado para essa fila específica.

  • Vinculação de núcleo de CPU: Quando uma fila recebe um pacote, ela dispara uma interrupção. O núcleo de CPU vinculado processa essa interrupção.

    Imagens diferentes do Red Hat Enterprise Linux suportam afinidade de interrupção de rede por padrão. Cada fila está associada a uma interrupção independente, e a afinidade de interrupção distribui o tratamento entre diferentes núcleos de CPU. Consulte Mecanismo central de múltiplas filas.

Ativar ou desativar RSS personalizado para uma ENI

Importante

O recurso de RSS personalizado para ENIs está em preview por convite. Para utilizar este recurso, envie um ticket.

Ao ativar o RSS personalizado em um tipo de instância compatível, o tráfego recebido é distribuído para múltiplas filas de recebimento com base nas regras de hash padrão. Diferentes núcleos de CPU processam as filas em paralelo, aumentando o throughput e reduzindo a carga em núcleos individuais.

Pré-requisitos

  • O RSS personalizado está sendo lançado em fases e encontra-se disponível nas seguintes regiões:

    Nome da Região

    ID da Região

    China (Hong Kong)

    cn-hongkong

  • Apenas algumas famílias de instâncias com múltiplas filas suportam RSS personalizado, incluindo a família de instâncias de uso geral g9i e a família de instâncias otimizadas para memória r9i.

    Chame a operação de API DescribeInstanceTypes para verificar o suporte. RssSupport=true indica que o recurso é suportado.

  • Recomenda-se a imagem Alibaba Cloud Linux 3.2104 LTS 64 bits.

    • Se você utilizar outras imagens públicas, a versão do kernel deve ser 6,12 ou superior.

    • Para usar o recurso de RSS personalizado em uma aplicação DPDK, a versão do DPDK deve ser 21,11 ou superior.

Como ativar ou desativar

O RSS personalizado vem desativado por padrão. É possível ativá-lo ou desativá-lo durante a criação da ENI ou após sua criação.

  • Ao chamar CreateNetworkInterface, defina EnableRss em EnhancedNetwork como true ou false.

    O RSS personalizado entra em vigor após a anexação da ENI a uma instância.

  • Chame ModifyNetworkInterfaceAttribute e defina EnableRss em EnhancedNetwork como true ou false. Após ativar ou desativar, a alteração entra em vigor quando a ENI é reanexada:

  • Chame DescribeNetworkInterfaceAttribute com Attribute definido como enhancedNetwork para consultar se o RSS personalizado está ativado. EnableRSS=true significa ativado; false significa desativado.

    Se o RSS não tiver sido modificado para a ENI, esta operação não retornará o parâmetro EnableRSS, indicando que o recurso não está ativado.

Visualizar a configuração de RSS de uma ENI

Após ativar o RSS personalizado, o driver da ENI gera uma configuração de RSS padrão baseada no mecanismo e nas regras padrão quando a ENI é recarregada.

Faça login remoto em uma instância Linux e execute ethtool -x eth0 para visualizar a configuração de RSS da ENI primária. Para uma ENI secundária, substitua o identificador da interface (eth1, eth2, etc.).

image

  • Tabela de hash (RX flow hash indirection table): Define as regras de mapeamento dos valores de hash para as 64 filas de recebimento.

    • Cada linha na tabela de hash representa um intervalo de valores de hash, indicado pelo índice inicial. Por exemplo, 0 indica valores de hash de 0 a 7, e 8 indica de 8 a 15.

    • Os números na tabela (0, 1, 2, e assim por diante) representam os IDs das filas de recebimento correspondentes. Neste exemplo, existem 64 filas (0-63).

    • Configuração atual: A tabela de indireção é preenchida ciclicamente com a sequência 0,1,2,...,63,0,1,2...63, garantindo que os valores de hash sejam mapeados uniformemente para as 64 filas e que o tráfego seja distribuído de forma equilibrada.

  • Chave de hash RSS: A chave usada para calcular o valor de hash. Os caracteres hexadecimais representam uma chave de 40 bytes.

  • Função de hash RSS: Define o algoritmo para cálculo do valor de hash.

    • toeplitz: Este é o algoritmo padrão e está atualmente ativado. Suporta hash simétrico baseado em tupla-5 e é adequado para tráfego de uso geral.

    • xor/crc32: São outros algoritmos opcionais, tipicamente usados em cenários específicos. O ECS atualmente suporta apenas o algoritmo toeplitz, e outros algoritmos não são suportados nem ativados.

  • Regras de hash: Visualize a configuração de campos de hash para o tráfego recebido pela ENI primária.

    ethtool  -n eth0 rx-flow-hash tcp4
    • Substitua tcp4 pelo tipo de protocolo que deseja consultar, como udp4, tcp6 ou udp6.

    • As regras de hash padrão para RSS de ENI ECS são as seguintes e não podem ser alteradas.

      • Tráfego TCP e UDP sobre IPv4/IPv6: O valor de hash é calculado com base na tupla-4 (IP de origem, IP de destino, porta de origem e porta de destino) e na chave de hash.

      • Tráfego não TCP/UDP sobre IPv4/IPv6, como ICMP: O valor de hash é calculado com base na tupla-2 (IP de origem e IP de destino) e na chave de hash.

      • Mensagens sem IP (como ARP) são enviadas para a fila padrão (fila 0) e não passam pelo processo de hash.

    • Tomando tcp4 como exemplo, para tráfego TCP IPv4, o valor de hash é calculado por padrão com base na tupla-4 (IP de origem, endereço IP de destino, porta de origem, porta de destino) e na chave de hash:

      image

      As características esperadas do tráfego são:

      • Mesma conexão TCP: Todos os pacotes possuem o mesmo valor de hash e são distribuídos para a mesma fila, garantindo a ordenação dos pacotes.

      • Conexões TCP diferentes: Se as tuplas-4 forem diferentes, os valores de hash serão distintos. O tráfego é distribuído para filas diferentes, permitindo que múltiplos núcleos de CPU processem conexões distintas em paralelo e aumentem o throughput.

Se a mensagem abaixo for retornada, o RSS personalizado não está ativado ou o tipo de instância não oferece suporte. Ative o RSS personalizado após confirmar que o tipo de instância é compatível.

image

Configurar RSS personalizado para uma ENI

A configuração padrão de RSS atende à maioria dos requisitos. No entanto, pode ser necessário ajustar a chave de hash ou a tabela de indireção nas seguintes situações:

  • Tráfego desigual: Os valores de hash padrão podem concentrar tráfego específico em poucas filas.

  • Otimização de desempenho: Ajuste as regras de hash com base nas características do tráfego, como alta proporção de UDP, para melhorar a utilização das filas.

  • Requisitos de segurança: Utilize uma chave de hash personalizada para impedir que atacantes prevejam a distribuição do tráfego.

Importante
  • Neste exemplo, a instância está configurada com a imagem Alibaba Cloud Linux 3.2104 LTS 64-bit.

  • Este exemplo utiliza a ENI primária eth0. Substitua o identificador da interface por eth1 ou eth2 para modificar o RSS da ENI secundária correspondente.

  • Quando a configuração de RSS não corresponde às características do seu tráfego, isso pode causar distribuição desigual e contenção de estado entre núcleos, afetando o desempenho. Faça ajustes com base nas características reais do tráfego e utilize ferramentas de monitoramento para observar a distribuição das filas em tempo real.

Configurar a chave de hash

Regenere a chave de hash RSS se o tráfego estiver distribuído de forma desigual (por exemplo, rx_packets de algumas filas significativamente maior que outras) ou para evitar que atacantes deduzam padrões de tráfego através de engenharia reversa do valor de hash.

Importante

Modificar a chave altera os valores de hash das conexões existentes, o que pode causar reordenação temporária de pacotes ou retransmissões. Execute este procedimento fora dos horários de pico.

  1. Visualize a chave de hash atual da ENI.

    O driver da ENI gera uma chave de hash padrão de 40 bytes quando a ENI é carregada.

    ethtool -x eth0

    image

  2. Gere uma nova chave aleatória usando OpenSSL.

    openssl rand -hex 40 | fold -w2 | paste -sd: -
  3. Aplique a nova chave à ENI.

    Importante

    Esta é uma configuração temporária. Ela se torna inválida após reiniciar a instância ou reanexar a ENI. O driver da ENI inicializa automaticamente uma configuração padrão aleatória.

    ethtool -X eth0 hkey <hash key>

    Substitua <hash key> pela nova chave gerada na etapa anterior.

  4. Verifique se a nova chave entrou em vigor.

    ethtool -x eth0

    A saída mostra que a nova chave entrou em vigor:

    image

Configurar campos de hash

Para isolar tráfego específico ou usar apenas endereços IP de origem/destino para hashing, modifique os identificadores de campo. Isso otimiza a distribuição RSS para diferentes cenários, como alto throughput, baixa latência ou hash simétrico.

  1. Visualize a configuração de campos de hash para o tráfego recebido pela ENI primária.

    ethtool  -n eth0 rx-flow-hash tcp4
    • Substitua tcp4 por um protocolo diferente, como udp4, tcp6, udp6, .

    • Tome tcp4 como exemplo. De acordo com o Mecanismo e Regras Padrão, para tráfego TCP IPv4, um valor de hash é calculado por padrão com base na tupla-4 (IP de origem, endereço IP de destino, porta de origem e porta de destino) e na chave de hash. As características esperadas do tráfego são:

      • Mesma conexão TCP: Todos os pacotes possuem o mesmo valor de hash e são distribuídos para a mesma fila, garantindo a ordenação dos pacotes.

      • Conexões TCP diferentes: Se as tuplas-4 forem diferentes, os valores de hash serão distintos. O tráfego é distribuído para filas diferentes, permitindo que múltiplos núcleos de CPU processem conexões distintas em paralelo e aumentem o throughput.

    image

  2. Modifique as regras de hash.

    Importante

    Esta é uma configuração temporária. Ela se torna inválida após reiniciar a instância ou reanexar a ENI. O driver da ENI inicializa automaticamente uma configuração padrão aleatória.

    ethtool -N eth0 rx-flow-hash <Protocol Type> <field identifier>
    • Tipo de Protocolo: udp4, tcp6 ou udp6.

    • Identificadores de campo comuns estão listados abaixo. Combine-os conforme necessário.

      Identificador

      Significado

      Tipo de Protocolo Aplicável

      s

      Endereço IP de Origem

      IPv4/IPv6 (todos os protocolos)

      d

      Endereço IP de Destino

      IPv4/IPv6 (todos os protocolos)

      f

      Campos completos da Camada 4 (transporte): porta de origem + porta de destino (TCP/UDP/SCTP)

      TCP4/6, UDP4/6, SCTP4/6

      r

      Prioridade VLAN

      Protocolos que suportam VLAN

      m

      Campos de Cabeçalho Interno para encapsulamento de túnel

      Protocolos de tunelamento, como VXLAN e GRE

      n

      Sem Flag: Alguns drivers exigem este parâmetro para concluir a configuração.

      Geral

    • Para usar apenas IP de origem + IP de destino para TCP sobre IPv4:

      sudo ethtool -N eth0 rx-flow-hash tcp4 sd

      image

    • Restaure o campo de hash tcp4 padrão (tupla-4: IP de origem/destino, porta de origem/destino):

      sudo ethtool -N eth0 rx-flow-hash tcp4 sdfn

Configurar a tabela de indireção

O modo de distribuição da tabela de indireção RSS é o mecanismo central que mapeia valores de hash para filas de recebimento específicas. Utilizando uma tabela de indireção predefinida, o resultado do cálculo de hash é convertido em um índice de fila, permitindo uma distribuição flexível do tráfego entre múltiplos núcleos.

Diferentes modos de distribuição, como distribuição uniforme e ponderada, afetam diretamente a forma como o tráfego é distribuído entre os núcleos de CPU. Isso impacta o throughput do sistema, a latência, a utilização de recursos e o desempenho do serviço.

Escolha um modo de distribuição adequado ao seu cenário.

Importante

As configurações a seguir são temporárias. Elas se tornam inválidas após reiniciar a instância ou reanexar a ENI. O driver da ENI reinicializa uma configuração padrão aleatória.

  • Modo de distribuição uniforme: As primeiras N filas preenchem os buckets de hash em loop. Adequado para cenários de alta concorrência geral.

    ethtool -X eth0 equal <Number of queues N>

    Se o valor for definido como 64, a tabela de indireção será preenchida em loop com 0,1,2,...,63,0,1,2...63,.

    image

  • Distribuição ponderada: Aloca buckets de hash com base em proporções de peso. Ideal para cenários com prioridades de negócios diferenciadas (por exemplo, fila 0 processa tráfego em tempo real e fila 1 processa tarefas em segundo plano) ou desempenho misto de CPU.

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

    No exemplo a seguir, a fila 0 recebe 60% e a fila 1 recebe 40%:

    Nota

    Se houver quatro filas no total, mas você definir pesos apenas para duas filas, a tabela de indireção gerará mapeamentos apenas para a fila 0 e a fila 1.

    ethtool -X eth0 weight 6 4

    image

  • Distribuição parcial de filas: A partir de uma fila especificada, usa filas consecutivas para preencher buckets de hash em loop. Recomendado para direcionar tráfego a um intervalo específico de CPU, como um nó NUMA.

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

    No exemplo a seguir, as filas 2-41 (40 filas começando da fila 2) preenchem os buckets de hash:

    ethtool -X eth0 start 2 equal 40

    image

Usar watch para observar a distribuição de tráfego

Após ativar o RSS personalizado na ENI, use hping3 para gerar tráfego com diferentes características e watch para monitorar a distribuição de interrupções das filas, verificando se o tráfego está sendo distribuído para múltiplos núcleos conforme esperado.

Pré-requisitos

Adquira duas instâncias ECS com as seguintes configurações:

  • Instância ECS remetente (10.0.0.252): Uma instância com hping3 instalado para gerar tráfego com diferentes características.

  • Instância ECS receptora: Uma instância cujo tipo suporta múltiplas filas para ENIs, com uma ENI secundária anexada (10.0.0.5) e RSS personalizado ativado na ENI secundária eth1.

    Nota
    • Este exemplo utiliza quatro filas para teste.

    • Para eliminar o impacto do tráfego de login SSH na ENI primária, o RSS personalizado é ativado na ENI secundária eth1 para os testes.

  • Conectividade de rede: As duas instâncias ECS devem estar no mesmo grupo de segurança e poder se comunicar pela rede interna.

Procedimento

  1. Faça login na instância ECS receptora, verifique a configuração de RSS da ENI secundária e confirme a distribuição de tráfego esperada. Consulte Visualizar a configuração de RSS de uma ENI.

    image

  2. Faça login na instância ECS remetente e instale o hping3.

    yum install -y hping3
  3. Na instância ECS receptora, monitore a contagem de pacotes das filas em tempo real.

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

    Substitua a configuração do número da fila pela configuração real da ENI receptora.

  4. Faça login na instância ECS remetente e simule diferentes padrões de tráfego.

    • Cenário 1: Envie 10.000 pacotes SYN em alta velocidade para o endereço IP do receptor com portas de destino aleatórias. Isso garante que o hash do tráfego seja disperso e verifica o balanceamento 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: Porta de destino aleatória

      • -p 0: Usado com rand-dest

      • --baseport 10000: O valor inicial para a porta de origem.

      • -c 10000: Envia 10.000 pacotes

      • -i u100: Especifica um intervalo de 100 microssegundos entre pacotes para envio em alta velocidade.

      • -I eth0: Usado com rand-dest para especificar a interface de rede do remetente

      Na instância receptora, a contagem de pacotes das quatro filas deve aumentar uniformemente:

      image

    • Cenário 2: Envie tráfego com endereço IP de origem e porta fixos. Isso testa se o mesmo tráfego é mapeado para a mesma fila e verifica a consistência do hash.

      O comando a seguir envia 10.000 pacotes TCP SYN para o alvo 10.0.0.5:80, com o IP de origem fixo em 10.0.0.252 e a porta de origem fixa em 12345.

      sudo hping3 10.0.0.5 -S -p 80 -c 10000 -s 12345 -a 10.0.0.252 --keep -i u100
      • O valor do índice de hash é calculado com base no script RSS. Espera-se que o tráfego seja processado pela fila 0:

        image

      • Resultado da observação real na instância ECS receptora (10.0.0.5):

        image

  5. Se a distribuição de tráfego não atender às expectativas (por exemplo, uma fila com carga significativamente maior), otimize ajustando a chave de hash ou configurando a tabela de indireção.

  6. Configurar e usar RSS no DPDK

    O Data Plane Development Kit (DPDK) é um framework de aceleração de plano de dados em modo usuário open-source que utiliza drivers em modo usuário, cópia zero e modo de polling para alcançar processamento de pacotes próximo à velocidade da linha. É adequado para domínios sensíveis a throughput e latência, como nuvens de telecomunicações, fintech e computação de borda. Consulte Data Plane Development Kit (DPDK*).

    Após ativar o RSS personalizado na ENI, utilize testpmd e l3fwd no DPDK para testar e validar a distribuição RSS.

    Instalar e configurar DPDK em uma instância ECS

    Nota
    • Para usar o recurso de RSS personalizado em uma aplicação DPDK, a versão do DPDK deve ser 21,11 ou superior.

    • Este tópico usa uma instância ecs.r9i.16xlarge (64 filas) executando a imagem Alibaba Cloud Linux 3.2104 LTS 64-bit como exemplo para demonstrar a instalação do DPDK versão 22.11.3.

    • Este exemplo utiliza a ENI primária eth0. Substitua o identificador da interface por eth1 ou eth2 para modificar o RSS da ENI secundária correspondente.

    Etapa 1: Instalar DPDK

    Clique para visualizar as etapas de exemplo para instalar o DPDK

    1. Atualize o sistema e instale as ferramentas básicas.

      sudo yum update -y
      sudo yum install -y git wget gcc make kernel-devel-$(uname -r) numactl-devel python3 pciutils
    2. Instale as dependências do DPDK.

      sudo yum install -y libpcap-devel meson ninja-build
    3. Configure huge pages.

      • O DPDK ignora a pilha de protocolos do kernel para operar diretamente a ENI. Ele usa huge pages (tipicamente 2 MB) em vez de páginas de 4 KB para reduzir falhas de TLB e melhorar a velocidade de acesso à memória.

      • Alocar muitas huge pages reduz a memória normal disponível para o SO. Isso pode causar falhas em outras aplicações ou serviços do sistema. A alocação excessiva também pode impedir a conectividade da instância.

      • Calcule o número necessário de huge pages com base nas necessidades de memória da sua aplicação.

        Número de huge pages = Memória da aplicação / Tamanho da página. O tamanho padrão da página é 2 MB. Por exemplo, 16 GB / 2 MB = 8192 páginas.

      echo "vm.nr_hugepages = 8192" | sudo tee -a /etc/sysctl.conf
      sudo sysctl -p
    4. Crie e monte o diretório de huge pages.

      sudo mkdir -p /dev/hugepages
      sudo mount -t hugetlbfs hugetlbfs /dev/hugepages
    5. Baixe o código-fonte do DPDK. É necessária uma conexão com 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 o 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

      Se encontrar o erro missing python module: elftools mostrado na figura abaixo durante a compilação,

      image

      Especifique sua versão do Python, instale pyelftools e recompile:

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

    Etapa 2: Carregar módulos do kernel

    O DPDK requer módulos do kernel como UIO ou VFIO para acesso a dispositivos em modo usuário. O VFIO é preferido por sua segurança (depende do IOMMU), enquanto o UIO serve para testes rápidos. Este exemplo usa VFIO.

    1. Ative o IOMMU.

      O VFIO depende do IOMMU para vinculação segura de dispositivos em modo usuário e mapeamento DMA.

      1. Abra o arquivo.

        sudo vim /etc/default/grub
      2. Pressione i para entrar no modo de inserção. Adicione intel_iommu=on ao parâmetro GRUB_CMDLINE_LINUX. Salve e feche o arquivo.

        Exemplo da configuração modificada:grub-config

      3. Aplique a configuração.

        sudo grub2-mkconfig -o /boot/grub2/grub.cfg

        image.png

      4. Reinicie a instância e reconecte-se após a inicialização.

        reboot
        Aviso

        A operação de reinicialização interrompe a instância por um curto período e pode interromper serviços em execução. Recomendamos reiniciar a instância fora dos horários de pico.

    2. Instale os drivers VFIO e VFIO-PCI.

      sudo modprobe vfio && \
      sudo modprobe vfio-pci
    3. Ative o noiommu_mode.

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

    Etapa 3: Vincular a ENI ao driver DPDK

    1. Ative o RSS personalizado na ENI.

      Certifique-se de que o RSS personalizado esteja ativado para a ENI que o DPDK assumirá e que a ENI tenha sido reanexada para que a alteração tenha efeito.

    2. Conecte-se a uma instância usando VNC.

    3. Visualize o status de vinculação do driver do dispositivo PCI. Por padrão, a ENI é gerenciada pelo kernel.

      dpdk-devbind.py --status

      image

      • O dispositivo 0000:00:05.0 é gerenciado pelo driver de kernel virtio-pci, e eth0 está ativa.

      • O dispositivo pode ser alternado para vfio-pci para controle em modo usuário pelo DPDK. Primeiro desative a interface e desvincule o driver do kernel.

    4. Desative a eth0.

      sudo ip link set dev eth0 down

      Se você pular esta etapa, a vinculação ao VFIO falhará:

      image

    5. Desvincule o driver do kernel e vincule ao VFIO.

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

      Substitua o identificador do dispositivo PCI pelo que você consultou para sua ENI.

      Nota

      Para revincular o driver do kernel, pare a aplicação DPDK com sudo pkill dpdk-app e execute dpdk-devbind.py -b virtio-pci 0000:00:05.0.

    6. Visualize novamente o status de vinculação do driver do dispositivo PCI. A ENI agora deve estar sob controle do DPDK.

      Importante

      Quando a ENI é assumida pelo driver em modo usuário do DPDK, o kernel deixa de controlá-la. Comandos como ip a não conseguirão exibir suas informações.

      dpdk-devbind.py --status

      image

      O dispositivo 0000:00:05.0 agora está vinculado ao vfio-pci.

    Configurar RSS usando testpmd

    O Testpmd é uma ferramenta de teste do DPDK para verificar recursos do driver da ENI e depurar aplicações de plano de dados. Consulte Testpmd Runtime Functions.

    Importante

    Modificações na configuração da ENI (contagem de filas, regras RSS, tabela RETA) só entram em vigor após iniciar ou reiniciar o encaminhamento de pacotes.

    1. Conecte-se a uma instância usando VNC.

    2. Inicie a ferramenta de teste de encaminhamento de pacotes testpmd do 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: Anexa a ENI no endereço PCI 0000:00:05.0 ao DPDK. Execute dpdk-devbind.py --status para encontrar o endereço PCI.

      • --socket-mem 1024: Pré-aloca 1024 MB de memória de huge page por nó NUMA.

      • -i: Inicia o modo de linha de comando interativo para ajustar dinamicamente a configuração e visualizar estatísticas. Digite quit para sair.

      • --portmask=0x1: Habilita a máscara de porta 0x1 (binário 0001), usando apenas a primeira ENI (endereço PCI 0000:00:05.0).

        • Cada bit binário de portmask corresponde a uma porta (0x1 habilita a porta 0, 0x3 habilita as portas 0 e 1).

        • Apenas um dispositivo (0000:00:05.0) está anexado, portanto seu número de porta DPDK é 0.

      • --rxq=64 / --txq=64: Define a contagem de filas RX e TX por porta como 64 para processamento multifila. Substitua 64 pela contagem real de filas da ENI.

      • --forward-mode=rxonly: Modo somente recepção (pacotes são recebidos e descartados). Usado para testar o desempenho de recebimento da ENI ou para captura de pacotes.

    3. Entre no modo interativo e consulte a configuração atual de RSS.

      • Consultar informações de configuração de hash: show port info <port_id>

        port_id: A porta. Neste exemplo, porta 0.

        Clique para visualizar a saída de exemplo

        image

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

        image

      • Consultar configuração da tabela de indireção: show port <port_id> rss reta <size> <mask0, mask1...>

        • size: O número de entradas da tabela de indireção a serem consultadas. O valor é fixo em 128.

        • mask0, mask1: As máscaras usadas para filtrar o intervalo de índices de hash a serem exibidos, especificados em formato hexadecimal. Por exemplo, mask0=0xff indica que as entradas para índices de hash de 0 a 7 serão exibidas.

          O tamanho da tabela de indireção é fixo em 128. Duas máscaras são necessárias, cada uma cobrindo 64 blocos de índice. Execute show port 0 rss reta 128 (0xffffffffffffffff,0xffffffffffffffff) para retornar todos os 128 mapeamentos de índice para fila:

          Clique para visualizar a saída de exemplo

          image

    4. Configure o RSS conforme seus requisitos.

      • Configure uma nova chave de hash.

        Gere uma nova chave aleatória com 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, execute port config 0 rss-hash-key ipv4 6D5A56DA255B0EC24167253D43A38FB0D0CA2BCBAE7B30B477CB2DA38030F20C6A42B73BBEAC01FC para configurar a chave de hash e verifique:

        image

      • Configure a tabela de indireção RSS para mapear valores de hash para filas especificadas.

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

        Configure com base no número real de filas:

        • hash: O índice de hash. O intervalo depende do tamanho da tabela de indireção (por exemplo, 0-63 para 64 entradas).

        • queue: O número da fila de recebimento de destino.

    5. Aplicar RSS no l3fwd

      Para ativar o RSS em uma aplicação DPDK, implemente a configuração da chave de hash e da tabela de indireção no seu código. O exemplo a seguir utiliza o L3FWD.

      L3FWD (Layer 3 Forwarding) é uma aplicação de exemplo do DPDK que demonstra roteamento de pacotes baseado em IP de alto desempenho usando cópia zero e Polling Mode Drivers (PMDs).

      1. Modifique o código-fonte do L3FWD no DPDK (examples/l3fwd/main.c).

        • Modifique a seção de inicialização da porta static struct rte_eth_conf port_conf no código de exemplo do L3FWD.

          Clique para visualizar a modificação do 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,
          	},
          };
        • Adicione funções para configurar a tabela de indireção de hash e a chave de hash.

          Clique para visualizar o código adicionado

          /**
           * @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;
              }
          }
        • Após rte_eth_dev_start, chame as novas funções de configuração.

          Clique para visualizar o código de chamada no 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. Recompile o L3FWD após modificar o código-fonte.

        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 as vinculações porta-fila-núcleo e inicie o 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 tripla especifica:

          • PORT_ID: O ID da porta (começa em 0).

          • QUEUE_ID: O ID da fila de recebimento (começa em 0).

          • LCORE_ID: O ID do núcleo lógico (começa em 0).

        • O exemplo a seguir usa dois núcleos e duas filas:

          ./dpdk-l3fwd --legacy-mem -a 0000:00:05.0 --socket-mem 1024 -- -p 0x1 --config="(0.0.0),(0.1.1)"  --parse-ptype
          • A primeira tripla (0,0,0): O núcleo lógico 0 (lcore0) processa a fila 0 da porta 0.

          • A segunda tripla (0,1,1): O núcleo lógico 1 (lcore1) processa a fila 1 da porta 0.

          image

      Usar o script RSS para calcular um índice de hash

      Utilize este script Python para calcular para qual fila de recebimento um pacote é mapeado, dada sua tupla-4 e chave de hash. O resultado orienta a configuração da tabela de indireção para direcionar fluxos específicos para filas designadas.

      Clique para visualizar o conteúdo do 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()

      Clique para visualizar o uso do script

      Visualize o uso do script: python ali_ecs_rss_calc.py -h.

      image

      O exemplo a seguir utiliza a configuração de RSS de uma ENI com 64 filas:

      image

      Calcule o valor de hash RSS usando o script com as seguintes informações de tupla-5 (modifique os valores conforme necessário).

      • Endereço IP de destino (-r): 10.0.0.1, que é o endereço IP da ENI da instância receptora com RSS configurado.

      • Endereço IP de origem (-t): 10.0.0.251, que é o endereço IP da ENI do remetente.

      • Porta de destino (-R): 26000

      • Porta de origem (-T): 18042

      • Chave de hash: Obtenha da configuração da tabela de indireção 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

      O script calcula o hash usando o algoritmo toeplitz. No resultado abaixo, o sistema usa um comprimento de tabela de indireção de 128. Um índice de hash de 117 significa que o pacote é mapeado para a fila no índice 117.

      image

      Visualize a tabela de indireção: o índice 117 mapeia para a fila 53, então os pacotes são processados pela fila 53.

      image