Trabalhar com o DPDK
Esta página descreve como usar o kit de desenvolvimento de plano de dados (DPDK, na sigla em inglês) em instâncias do Compute Engine U4C.
Sobre o DPDK no AF_XDP
O kit de desenvolvimento de plano de dados (DPDK) é um framework para aplicativos com alto desempenho que exigem processamento de pacotes rápido, baixa latência e desempenho consistente. O DPDK ignora a pilha de rede do kernel do Linux e é executado diretamente no espaço do usuário. É possível executar o DPDK em instâncias U4C usando uma arquitetura AF_XDP.
O DPDK fornece um driver de modo de pesquisa AF_XDP (PMD, na sigla em inglês), que é um dispositivo virtual (vdev) que permite que aplicativos DPDK sejam executados no AF_XDP no modo de cópia ou de cópia zero. Para mais informações, consulte
Driver de modo de pesquisa AF_XDP na
documentação do DPDK. Ao contrário das
implantações típicas no Compute Engine,
o DPDK no AF_XDP em instâncias U4C não exige a configuração de VFIO, UIO ou
dpdk-devbind.py.
O uso do DPDK com a solução ULL inclui suporte ao direcionamento de fluxo. É possível ignorar o hash de escalonamento do lado de recebimento (RSS, na sigla em inglês) padrão direcionando fluxos de tráfego específicos diretamente para uma fila de recebimento (RX) designada. A solução ULL oferece suporte ao direcionamento de fluxo de 3 tuplas (protocolo, endereço IP de destino e porta de destino) para tráfego de transmissão única e multicast ULL.
Antes de começar
Antes de trabalhar com o DPDK em instâncias do Compute Engine U4C, você precisa atender aos seguintes requisitos.
Criar uma instância U4C
Crie uma instância bare metal U4C, caso ainda não tenha feito isso. Consulte Criar ULL instâncias do Compute Engine.
Conectar-se à instância usando SSH
Conecte-se à instância usando SSH, caso ainda não tenha feito isso.
Mudar para o usuário raiz
Os comandos e scripts nos procedimentos a seguir modificam as configurações no nível do sistema, os parâmetros do kernel e as interfaces de rede. Para executá-los com sucesso, é necessário executá-los como o usuário raiz. É possível mudar para um shell raiz executão
sudo su, ou adicionar sudo antes de executar comandos conforme necessário.
Instalar o DPDK na instância U4C
Para instalar o DPDK na instância U4C, siga estas etapas:
Configure as dependências para a instalação do DPDK:
apt-get update && apt-get upgrade -yq apt-get install -yq build-essential ninja-build python3-pip \ linux-headers-$(uname -r) pkg-config libnuma-dev pip install pyelftools mesonInstale o DPDK.
wget https://fast.dpdk.org/rel/dpdk-VERSION.tar.xz tar xvf dpdk-VERSION.tar.xz cd dpdk-VERSION
Substitua
VERSIONpela versão do DPDK que você quer instalar, como26.07. Se necessário, consulte a página de download do DPDK.Para criar o DPDK com os exemplos:
meson setup -Dexamples=all build ninja -C build install; ldconfig
Configurar interfaces de rede para AF_XDP
Para usar o AF_XDP na NIC virtual do Google (gVNIC), é necessário ajustar os recursos do driver padrão para preparar a interface de rede.
É possível realizar essas etapas manualmente ou usar um script de configuração automatizado. Selecione uma das seguintes guias:
Manual
Siga estas etapas para cada interface de rede que você quer configurar.
Reduza as contagens de filas de RX e TX:a gVNIC usa por padrão o número máximo de filas de RX e TX com suporte, mas é necessário reduzir esse número pela metade para garantir que haja filas de TX suficientes para o tráfego normal do kernel.
ethtool -L NIC_NAME rx NUM_SOCKETS \ tx NUM_SOCKETS
Substitua:
NIC_NAME: o nome do SO da interface de rede, comoeth1.NUM_SOCKETS: o número de soquetes AF_XDP a serem configurados. Defina um valor não maior que metade das filas máximas da interface. Para instâncias U4C, esse valor é normalmente8(metade das 16 filas padrão). É possível verificar as filas máximas executandoethtool -l NIC_NAME.
Desative o GRO e o LRO de hardware:como a gVNIC não oferece suporte ao XDP de vários buffers, é necessário desativar a descarga de recebimento grande (LRO, na sigla em inglês) e a descarga de recebimento genérico (GRO, na sigla em inglês) de hardware:
ethtool -K NIC_NAME rx-gro-hw off ethtool -K NIC_NAME lro off
Reduza o comprimento do buffer de RX:por padrão, os drivers mais recentes postam buffers de 4 KB (4.096 bytes) na interface de rede para RX, mas o XDP exige um comprimento de buffer de
2048:ethtool -G NIC_NAME rx-buf-len 2048
Script
Como alternativa, é possível executar o seguinte script Bash para cada interface de rede que você quer configurar. O script prepara automaticamente uma determinada interface de rede para XDP, reduzindo as contagens de filas, desativando as descargas e ajustando o comprimento do buffer de RX:
#!/bin/bash # Usage example: NUM_SOCKETS=8 prep_xdp.sh eth0 DEV=$1 NUM_SOCKETS=${NUM_SOCKETS=1} # Reduce RX/TX queue counts to the number of AF_XDP sockets ethtool -L $DEV rx $NUM_SOCKETS tx $NUM_SOCKETS # Disable LRO/HW-gro OFFLOAD=$(ethtool -k $DEV | \ grep "rx-gro-hw\|large-receive-offload" | \ grep -v fixed | cut -d ":" -f 1) ethtool -K $DEV ${OFFLOAD} off # Reduce RX buffer length to 2048 ethtool -G $DEV rx-buf-len 2048
Executar o aplicativo DPDK
Para usar o PMD AF_XDP, inclua a flag --vdev nos argumentos da camada de abstração de ambiente (EAL, na sigla em inglês) do aplicativo DPDK.
O comando de exemplo a seguir inclui vários parâmetros importantes. Para informações detalhadas sobre a configuração e os parâmetros, consulte Driver de modo de pesquisa AF_XDP na documentação do DPDK.
DPDK_APPLICATION -a PCIE_BDF \ --vdev=net_af_xdp,iface=NIC_NAME,queue_count=NUM_SOCKETS,start_queue=START_QUEUE,xdp_prog=XDP_PROG \ -- APPLICATION_ARGS
Substitua:
DPDK_APPLICATION: o binário do aplicativo DPDK a ser executado.PCIE_BDF: o endereço PCI da interface de rede. É possível encontrar esse valor executandoethtool -i NIC_NAMEe verificando o valorbus-info, como0000:00:04.0.NIC_NAME: o nome do SO da interface de rede, comoeth1.NUM_SOCKETS: o número de soquetes AF_XDP a serem abertos. Cada soquete é anexado a um único par de filas. Esse valor precisa corresponder à contagem de filas configurada na interface.START_QUEUE: o índice da fila inicial para os soquetes AF_XDP.XDP_PROG: um programa XDP personalizado a ser executado em pacotes recebidos. Se omitido, o DPDK usará o programa XDP padrão fornecido porlibxdp.APPLICATION_ARGS: argumentos específicos do aplicativo DPDK.
Usar recursos do driver
Esta seção fornece informações de uso para direcionamento de fluxo e marcação de data/hora de RX.
Direcionamento de fluxo
É possível usar o direcionamento de fluxo com o XDP para direcionar pacotes de aplicativos a um subconjunto específico de filas, deixando as filas restantes para o tráfego do kernel. As seções a seguir descrevem duas abordagens que podem ser usadas para a programação de regras de fluxo.
Direcionamento de fluxo pré-programado (recomendado)
Como a programação de fluxo on-the-fly usando chamadas ioctl não é compatível com o PMD AF_XDP e exige a modificação do aplicativo DPDK, recomendamos pré-programar as regras de fluxo usando ethtool antes de iniciar o aplicativo.
Por exemplo, para programar uma regra de fluxo que direciona o tráfego UDP IPv4 para a fila 0:
ethtool -N NIC_NAME flow-type udp4 \ dst-ip DST_IP dst-port DST_PORT action 0 loc 0
Substitua:
NIC_NAME: o nome do SO da interface de rede, comoeth1.DST_IP: o endereço IP de destino do tráfego a ser direcionado.DST_PORT: a porta de destino do tráfego a ser direcionado.
Programar regras de fluxo on-the-fly
O PMD AF_XDP não oferece suporte à programação de fluxo on-the-fly. Para usar esse método, é necessário modificar o aplicativo DPDK para enviar manualmente chamadas ioctl ethtool.
Recomendamos evitar essa abordagem,a menos que o aplicativo processe um grande número de conexões temporárias. A gVNIC oferece suporte a até 20.000 regras de direcionamento de fluxo de 3 tuplas. Se você precisar de menos regras e souber os endereços IP e as portas de destino com antecedência, pré-programe as regras.
Se você precisar programar regras dinamicamente, consulte o exemplo de código C a seguir:
Abrir para ver o exemplo de código C
struct flow_rule_info { uint32_t src_ip; uint32_t dst_ip; uint16_t src_port; uint16_t dst_port; uint32_t target_queue; uint32_t rule_id; } int add_flow_rule(const char *ifname, struct flow_rule_info *rule_info, bool is_5tuple) { struct ethtool_rxnfc cmd; struct ifreq ifr; int fd; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { fprintf(stderr, "Failed to open socket: %s", strerror(fd)); return -1; } memset(&cmd, 0, sizeof(cmd)); memset(&ifr, 0, sizeof(ifr)); cmd.cmd = ETHTOOL_SRXCLSRLINS; cmd.fs.flow_type = UDP_V4_FLOW; cmd.fs.h_u.udp_ip4_spec.ip4dst = rule_info->dst_ip; cmd.fs.h_u.udp_ip4_spec.pdst = htons(rule_info->dst_port); cmd.fs.m_u.udp_ip4_spec.ip4dst = 0xFFFFFFFF; cmd.fs.m_u.udp_ip4_spec.pdst = 0xFFFF; if (is_5tuple) { cmd.fs.h_u.udp_ip4_spec.ip4src = rule_info->src_ip; cmd.fs.h_u.udp_ip4_spec.psrc = htons(rule_info->src_port); cmd.fs.m_u.udp_ip4_spec.ip4src = 0xFFFFFFFF; cmd.fs.m_u.udp_ip4_spec.psrc = 0xFFFF; } cmd.fs.ring_cookie = rule_info->target_queue; cmd.fs.location = rule_info->rule_id; strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); ifr.ifr_data = (void *)&cmd; int ret = ioctl(fd, SIOCETHTOOL, &ifr); if (ret) fprintf(stderr, "Failed to send ioctl: %s\n", strerror(errno)); close(fd); return ret; } int try_add_xdp_flow_rule(int port, struct flow_rule_info *rule_info) { char dev_name[RTE_ETH_NAME_MAX_LEN]; char *af_xdp_driver = "net_af_xdp"; struct rte_eth_dev_info dev_info; int err; if (!rte_eth_dev_is_valid_port(port)) return -1; err = rte_eth_dev_info_get(port, &dev_info); if (err) { fprintf(stderr, "Error getting info for port %d: %s\n", port, strerror(err)); return retval; } if (strncmp(dev_info.driver_name, af_xdp_driver, strlen(af_xdp_driver)) != 0) { fprintf(stderr, "Not an AF_XDP vdev!\n"); return -EINVAL; } err = rte_eth_dev_get_name_by_port(port, dev_name); if (retval) { fprintf(stderr, "Failed to get dev_name: %s\n", strerror(err)); return retval; } /* replace ens4 with correct ifname; must be passed in as application argument */ err = add_flow_rule("ens4", rule_info)); if (err) fprintf("Failed to add flow rule: %s\n", strerror(err)); return err; } /* From somewhere in the application: */ int application_func(...) { ... struct flow_rule_info *rule_info { .src_ip = 0x0a000001, .dst_ip = 0x0a000002, .src_port = 0x1110, .uint16_t dst_port = 0x1011, .uint32_t target_queue = 0, .uint32_t rule_id = 1, }; try_add_xdp_flow_rule(xdp_port_id, &rule_info, /*is_5tuple=*/false); ... }
Programação de RSS
Ao usar o direcionamento de fluxo, o RSS pode ajudar a fornecer um melhor isolamento de tráfego.
Assim como o direcionamento de fluxo, é possível configurar o RSS usando ethtool. Recomendamos configurar o RSS antes de executar o aplicativo.
O exemplo a seguir mostra como configurar o RSS para trabalhar com o AF_XDP:
# Example: Kernel queues 0-3, XDP queues 4-7 NUM_SOCKETS=4 bash prep_xdp.sh eth0 # Program flow rules ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_0 action 4 loc 0 ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_1 action 5 loc 1 ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_2 action 6 loc 2 ethtool -N eth0 flow-type udp4 dst-ip DST_IP dst-port DST_PORT_3 action 7 loc 3 # If there are more ports that the application polls on, flow rules can be added in a round-robin fashion in a script. # Program RSS ethtool -X eth0 start 0 equal 4 # Run the application ./path/to/application -a 0000:00:03.0 --vdev net_af_xdp,iface=eth0,start_queue=4,queue_count=4 -- APPLICATION_ARGS
Marcação de data/hora de RX
Embora a marcação de data/hora de RX não seja compatível com o repositório DPDK upstream para o PMD AF_XDP, é possível usar a marcação de data/hora de RX fazendo o seguinte:
Aplique os patches necessários à árvore de origem do DPDK e recompilando a origem do DPDK e o aplicativo. Esses patches adicionam suporte para
rte_eth_read_clocke carimbos de data/hora de RX emmbufsrecebidos.Use um programa XDP que carregue o carimbo de data/hora nos metadados.
Forneça os seguintes parâmetros
vdevAF_XDP adicionais ao iniciar o aplicativo DPDK:xdp_meta_rx_ts_offset: o deslocamento de bytes do início dos metadados XDP em que o valor do carimbo de data/hora de RX de 64 bits está localizado.xdp_meta_valid_hint_offset: (opcional) o deslocamento de bytes que abrange um campo de flag de 1 byte que indica se o carimbo de data/hora é válido.xdp_meta_rx_ts_valid_mask: (opcional) a máscara de bits usada para extrair os bits de flag válidos.Se
ctxfor o início dos metadados, o valor de(ctx->data_meta + xdp_meta_valid_hint_offset) & xdp_meta_rx_ts_valid_maskdescreve se o carimbo de data/hora é válido.
A seguir
- Para sincronizar o relógio da instância com o relógio da NIC física do servidor host, consulte Configurar a hora exata.